Publishing & Deployment Issues May 24, 2026

Steam Depot Promoted From Playtest Branch Ships Wrong build_label in Fest Demo - How to Fix

Fix Steam fest demo installs that still show playtest build_label or playtest-only defines after depot promotion. playtest_scope_map, separate CI lanes, VDF branch filters, and build_label smoke gates.

By GamineAI Team

Steam Depot Promoted From Playtest Branch Ships Wrong build_label in Fest Demo - How to Fix

Problem: You promoted the fest_demo depot for October Next Fest, but players install from the store and the title screen still shows playtest-wip-05 (or your internal QA label). Bug reports mention playtest-only menus, chapter six, or debug toggles that your fest scope doc excludes.

Who is affected now: Teams running the H2 2026 playtest vs fest demo isolation playbook who fixed branch names in Steamworks but still upload the same binary from the playtest CI job. SteamPipe accepted the depot swap—the exe inside still baked the playtest build_label and scripting defines.

Fastest safe fix: Read in-game build_label on the installed fest client → diff against playtest_scope_map_v1.json fest_public row → rebuild fest artifact from the fest-tagged commit (not playtest) → upload only to win64_fest_demo (or your fest depot ID) → re-run build_label_smoke_receipt_v1.json → block Wednesday promotion until label + surface match.

Direct answer

Depot promotion changes which files Steam serves—it does not rewrite strings inside your game. If the fest branch points at a manifest that still contains the playtest build, reviewers see playtest behavior under a fest URL. Fix artifact separation + scope map + CI gates, not the store page copy alone.

Why this issue spikes in June 2026

  1. Playtest isolation playbook adoption — Branches exist; binaries still shared.
  2. Same App ID, multiple depots — Easy to upload playtest output to fest depot “just to unblock QA.”
  3. CI “latest green” promotion — One workflow promotes whichever job finished last.
  4. No visible build_label in editor — Team smoke-tests Editor/Development; fest players use Steam install.
  5. Wednesday smoke now expects build_label + surface columns—mismatches surface in BUILD_RECEIPT diffs.

Distinct from dev console on playtest while fest is clean (debug surface), from save path after branch promotion (storage), and from GX.games receipt vs Steam build_label drift (cross-channel HTML5)—this article is version string + feature scope baked into the wrong Steam depot.

Symptoms and search phrases

  • Title screen / pause menu shows *`playtest-` label on fest_public** install.
  • Fest reviews cite features your fest hour-one scope excludes.
  • build_label in crash logs does not match BUILD_RECEIPT row for fest upload.
  • Playtest invite and fest demo share identical label—support cannot triage.
  • Steamworks branch says fest_demo; binary still includes PLAYTEST_BUILD define.
  • Producer promoted depot ID correctly; content root was playtest folder.
  • Streamer: “store demo is the playtest build.”

Root causes (check in order)

  1. Wrong content root uploaded — playtest dist/ zipped into fest depot.
  2. Shared steam_appid + define set — fest lane never stripped playtest flags.
  3. CI promotes latest playtest artifact — fest job skipped or failed silently.
  4. VDF branch filter misconfigured — fest branch still references playtest depot manifest.
  5. No in-game build_label readout — mismatch discovered by players, not QA.
  6. Copy-paste manifestfest_demo BuildID inherits playtest file list.
  7. playtest_scope_map stale — fest row documents label that was never shipped.

Beginner path (first 30 minutes)

Prerequisites: Steamworks access, fest demo installed from fest_demo branch (not a local zip), not the editor.

  1. Install from Steam → launch → note build_label on title or pause (photo).
  2. Open release-evidence/playtest/playtest_scope_map_v1.json → find fest_public row.
  3. Compare: label on screen must equal fest row, not playtest row.
  4. If mismatch → stop fest visibility toggle; do not patch marketing copy yet.
  5. Open last BUILD_RECEIPT / upload log—confirm which depot ID received bytes.

Common mistake: Validating build_label in Unity Play mode—use installed Steam build only.

Fastest safe fix path

Step 1 — In-game build_label readout smoke

Expose a string every fest build must set at compile time:

Engine Pattern
Unity Application.version + scripting define overlay, or UI Text bound to BuildInfo.Label
Godot Autoload BuildInfo.build_label from export preset env
Unreal BuildSettings.BuildVersion + optional CHANGELIST
GameMaker #macro BUILD_LABEL in one header
Construct / NW.js package.json version mirrored to HUD for smoke

Pass: Screenshot label on installed fest client before any SteamPipe upload.

Step 2 — Lock surfaces in playtest_scope_map_v1.json

{
  "schema": "playtest_scope_map_v1",
  "updated": "2026-05-24",
  "surfaces": [
    {
      "surface_id": "steam_playtest_invite",
      "branch": "playtest-invite-2026-q3",
      "depot_id": "1234568",
      "build_label": "playtest-invite-2026-05-24-rc3",
      "ruleset_id": "playtest_balance_q3",
      "demo_scope": "chapters_1_6_internal"
    },
    {
      "surface_id": "fest_public",
      "branch": "fest_demo",
      "depot_id": "1234569",
      "build_label": "fest-demo-2026-10-rc1",
      "ruleset_id": "fest_demo_2026_10",
      "demo_scope": "fest_demo_hour_one_only"
    }
  ]
}

Rule: build_label values must be unique per surface unless ruleset_id and demo_scope are intentionally identical (rare).

Step 3 — Separate CI lanes per surface

Workflow Triggers Upload target Must NOT
upload-playtest playtest-* tag win64_playtest depot Promote to fest
upload-fest fest-demo-* tag win64_fest_demo depot Reuse playtest artifact

Gate example (pseudo):

- name: Assert fest build_label
  run: |
    LABEL=$(strings ./out/Game.exe | grep -m1 '^fest-demo-' || true)
    test "$LABEL" = "fest-demo-2026-10-rc1"

Adapt per engine—goal is fail CI before SteamPipe, not after reviews.

Step 4 — SteamPipe / VDF branch discipline

  1. Steamworks → SteamPipe → Builds → confirm fest_demo build lists fest depot content hash.
  2. Compare manifest file list to playtest build—playtest_only.dat, debug menus, or extra chapters should be absent on fest.
  3. After upload, Set build live on branch fest_demo only—do not set playtest build live on fest branch “temporarily.”
  4. Regenerate library assets on the branch players download (Steamworks Builds doc).

Document manifest owner per ticket: default | fest_demo | playtest—see Steamworks distribution resource.

Step 5 — build_label_smoke_receipt_v1.json

{
  "schema": "build_label_smoke_receipt_v1",
  "checked_at_utc": "2026-05-24T20:00:00Z",
  "surfaces": [
    {
      "surface_id": "fest_public",
      "expected_build_label": "fest-demo-2026-10-rc1",
      "installed_build_label": "fest-demo-2026-10-rc1",
      "depot_id": "1234569",
      "branch": "fest_demo",
      "playtest_only_features_absent": true,
      "pass": true
    },
    {
      "surface_id": "steam_playtest_invite",
      "expected_build_label": "playtest-invite-2026-05-24-rc3",
      "installed_build_label": "playtest-invite-2026-05-24-rc3",
      "depot_id": "1234568",
      "branch": "playtest-invite-2026-q3",
      "pass": true
    }
  ],
  "cross_surface_label_collision": false,
  "pass": true
}

File beside BUILD_RECEIPT with surface column populated.

Step 6 — Wednesday metadata diff gate

Before fest branch promotion, diff includes:

Column fest_public playtest
build_label fest-demo-* playtest-*
surface fest_public steam_playtest_invite
depot_id fest depot playtest depot
ruleset_id fest ruleset playtest ruleset

See Wednesday demo build smoke ritual.

Working dev path — define and scope diff

Grep fest artifact for playtest leakage:

rg -i "playtest|debug_menu|chapter_6|cheat|DEVELOPMENT_BUILD" .\dist\fest\
Hit Action
PLAYTEST_BUILD define Re-export fest preset without define
playtest-wip in assets Wrong content root—rebuild fest lane
Extra .pak / bank Addressables group bled from playtest job
Same MD5 as playtest exe Do not promote—artifacts identical

Pair with save-slot label case study when UI slot numbers disagree with disk after promotion.

Verification checklist

  • [ ] Installed fest_public shows fest build_label on title/pause
  • [ ] Installed playtest shows different label (if both surfaces live)
  • [ ] playtest_scope_map_v1.json matches both installed labels
  • [ ] build_label_smoke_receipt_v1.json pass: true
  • [ ] No playtest-only menus/chapters on fest install
  • [ ] Steamworks fest_demo build references fest depot content hash
  • [ ] BUILD_RECEIPT row surface = fest_public for fest upload
  • [ ] Wednesday smoke diff includes build_label + surface

Prevention

  1. Two upload workflows — never “promote latest green” across surfaces.
  2. Photo gate — screenshot in-game label in CI artifact folder.
  3. Block fest visibility until receipt passes—metadata bar green is not enough.
  4. Train producers: depot ID swap ≠ new game build.
  5. Add build_label to playtest feedback forms and crash reports.

Troubleshooting

Symptom Fix
Label correct in zip, wrong on Steam Old build live on branch—set correct build live + library regen
Fest and playtest same label Rename one surface; rebuild both
Label correct, playtest features present Scope/ruleset leak—grep defines and content
Only internal QA sees issue They installed playtest branch—verify Steam branch selector
After hotfix, label unchanged Forgot to bump macro—increment fest rc suffix
Cloud saves cross surfaces See Steam Cloud overwrite help

FAQ

Is this the same as wrong Steam Input metadata on fest branch?
No—that is upload rejection / VDF on branch. Wrong build_label is binary content inside an otherwise accepted depot. See Next Fest demo Input metadata fix.

Can we hide the label for players?
You can—but you still need an internal readout for smoke. Hidden labels cause repeat promotions of the wrong exe.

Should playtest and fest share a depot to save space?
No. Separate depots are the primary guardrail; shared depots make this failure inevitable.

Unity: same version string, different defines?
Use distinct build_label strings beyond Application.version if defines differ—support triage needs human-readable separation.

Related links

Read the build_label on the installed fest client before you toggle fest visibility—SteamPipe green does not prove the right game shipped.