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
- Playtest isolation playbook adoption — Branches exist; binaries still shared.
- Same App ID, multiple depots — Easy to upload playtest output to fest depot “just to unblock QA.”
- CI “latest green” promotion — One workflow promotes whichever job finished last.
- No visible
build_labelin editor — Team smoke-tests Editor/Development; fest players use Steam install. - Wednesday smoke now expects
build_label+surfacecolumns—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_labelin 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 includesPLAYTEST_BUILDdefine. - Producer promoted depot ID correctly; content root was playtest folder.
- Streamer: “store demo is the playtest build.”
Root causes (check in order)
- Wrong content root uploaded — playtest
dist/zipped into fest depot. - Shared
steam_appid+ define set — fest lane never stripped playtest flags. - CI promotes latest playtest artifact — fest job skipped or failed silently.
- VDF branch filter misconfigured — fest branch still references playtest depot manifest.
- No in-game
build_labelreadout — mismatch discovered by players, not QA. - Copy-paste manifest —
fest_demoBuildID inherits playtest file list. playtest_scope_mapstale — 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.
- Install from Steam → launch → note
build_labelon title or pause (photo). - Open
release-evidence/playtest/playtest_scope_map_v1.json→ findfest_publicrow. - Compare: label on screen must equal fest row, not playtest row.
- If mismatch → stop fest visibility toggle; do not patch marketing copy yet.
- 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
- Steamworks → SteamPipe → Builds → confirm
fest_demobuild lists fest depot content hash. - Compare manifest file list to playtest build—
playtest_only.dat, debug menus, or extra chapters should be absent on fest. - After upload, Set build live on branch
fest_demoonly—do not set playtest build live on fest branch “temporarily.” - 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_labelon title/pause - [ ] Installed playtest shows different label (if both surfaces live)
- [ ]
playtest_scope_map_v1.jsonmatches both installed labels - [ ]
build_label_smoke_receipt_v1.jsonpass: true - [ ] No playtest-only menus/chapters on fest install
- [ ] Steamworks
fest_demobuild references fest depot content hash - [ ] BUILD_RECEIPT row
surface=fest_publicfor fest upload - [ ] Wednesday smoke diff includes
build_label+surface
Prevention
- Two upload workflows — never “promote latest green” across surfaces.
- Photo gate — screenshot in-game label in CI artifact folder.
- Block fest visibility until receipt passes—metadata bar green is not enough.
- Train producers: depot ID swap ≠ new game build.
- Add
build_labelto 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
- GX.games HTML5 Upload Receipt build_id vs Steam build_label
- Steam Playtest Build Exposes Dev Console While Fest Demo Is Clean
- Playtest vs Fest Demo Isolation Playbook (2026 H2)
- 12 Free Steam Playtest Fest Demo Isolation Runbooks
- 18 Free Steamworks PC Distribution Resources (October refresh)
- Wednesday Demo Build Smoke Ritual
- Your First BUILD_RECEIPT JSON and Upload Log
- Mismatched Save Slot Labels After Branch Promotion (case study)
- Steamworks — Branches
- Steamworks — Uploading to Steam (SteamPipe)
Read the build_label on the installed fest client before you toggle fest visibility—SteamPipe green does not prove the right game shipped.