Steam Playtest Build Exposes Developer Console While Fest Demo Branch Is Clean - How to Fix
Problem: Steam Playtest invite testers press F12, ~, or Ctrl+Shift+I and get a developer console or debug menu—but your fest_public build is supposed to be retail-clean. Support threads mix reports because both surfaces share one SKU story, or you promoted the wrong depot to the playtest branch.
Who is affected now: Micro-studios running the H2 2026 playtest vs fest demo isolation playbook while using Steam Playtest for closed QA and Next Fest for the public demo. The failure spikes when one CI job builds both surfaces with DEVELOPMENT_BUILD, config.developer, or NW.js devtools left on for “faster playtest iteration.”
Fastest safe fix: Split playtest_scope_map_v1.json so dev_console_allowed is true only on internal / beta_ffa rows → rebuild playtest invite from a tagged commit with retail defines → run console-open smoke (must fail on playtest if policy says no console) → verify fest_public unchanged on installed Steam client → file dev_console_ship_receipt_v1.json per surface before any branch promotion.
Direct answer
You have two player-facing surfaces sharing one mental model but not the same binary contract. Playtest invite builds often inherit debug scripting defines, steam_appid.txt, or develop folder exports; fest demo reviewers install fest_public and expect retail demo discipline. Fix depot + define separation, not player education.
Why this issue spikes in May 2026
- Playtest isolation became standard ops—teams map branches but skip per-surface console flags.
- NW.js / Electron fest exports ship Chromium devtools unless stripped on each lane.
- Unity
DEVELOPMENT_BUILDon playtest-only CI matrix rows leaks when the wrong artifact uploads. - Streamers and playtesters compare invite builds to store fest installs—mismatch reads as fraud.
- Wednesday smoke S7 now expects console checks—playtest-only leaks pollute triage.
Pair with 12 Free Steam playtest fest demo isolation runbooks and community playtest ops resource for surface tagging on feedback.
Symptoms and search phrases
- Playtesters report F12 opens devtools; fest demo on same SKU does not (or reverse).
build_labelon title matches playtest row but store page promises “fest demo only.”- Unity Development Build watermark on Playtest install only.
- Construct NW.js — Chromium inspector on playtest depot.
- Ren'Py
config.developertrue on playtest RPA only. - Fest reviewers: “playtesters had cheats; our build is clean” — both true, different depots.
- Refund / forum posts cite unfair demo after invite keys circulated.
Root causes (check in order)
- Shared CI job —
DEVELOPMENT_BUILD/DEBUGdefines on playtest matrix only, wrong artifact promoted. - Playtest depot promoted from
develop— not thefest_democommit you smoke-tested. playtestbranch inherits debug package — fest_public manifest points at retail, playtest at debug tree.steam_appid.txtleft in playtest root — launches wrong context / dev behaviors.dev_console_allowed: truein scope map but invite email promises retail parity.- Fest_public accidentally got debug build — less common; verify both surfaces.
- No
surfacecolumn on BUILD_RECEIPT — team cannot diff defines per upload.
Fastest safe fix path
Step 1 — Document 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": "win64_playtest",
"build_label": "playtest-invite-2026-05-24-rc3",
"dev_console_allowed": false,
"demo_scope": "chapters_1_3_balance_pass"
},
{
"surface_id": "fest_public",
"branch": "fest_public_2026_10",
"depot": "win64_fest_demo",
"build_label": "fest-demo-2026-10-rc1",
"dev_console_allowed": false,
"demo_scope": "fest_demo_hour_one_only"
}
]
}
Rule: If playtest QA needs console, use a separate beta_ffa row with dev_console_allowed: true—never the invite row players confuse with fest.
Step 2 — Split depots and scripting defines
| Engine | Playtest lane (retail policy) | Internal QA lane (console OK) |
|---|---|---|
| Unity | No DEVELOPMENT_BUILD; strip debug |
internal branch may use dev |
| Godot | export_presets.cfg debug=false for invite |
Debug preset on internal only |
| Construct NW.js | nwjs-config.json — disable devtools |
Separate export template |
| Ren'Py | config.developer = False in playtest build |
developer true on internal only |
Add CI step:
# Example Unity — fail if Development Build in playtest artifact
Select-String -Path ".\playtest_build\*.dll" -Pattern "Development Build" -Quiet
if ($?) { exit 1 }
Grep exported folders:
rg -i "devtools|debug_menu|window\.cheat|DEVELOPMENT_BUILD" .\dist\playtest\
Step 3 — Rebuild playtest from tagged commit
- Tag
playtest-invite-2026-05-24-rc3on the commit that passed define strip. - Upload only to
win64_playtestdepot on playtest-invite branch. - Remove stray
steam_appid.txtfrom retail playtest package if present. - Do not copy fest_public RPA/binary into playtest depot “to save time.”
Step 4 — Console smoke per surface
On installed Steam client (not editor):
| Surface | F12 / ~ / Ctrl+Shift+I | Pass criteria |
|---|---|---|
steam_playtest_invite |
Press each | Nothing opens if dev_console_allowed: false |
fest_public |
Press each | Nothing opens |
Log results in dev_console_ship_receipt_v1.json:
{
"receipt_type": "dev_console_ship_gate",
"version": "1.0.0",
"surfaces": [
{
"surface_id": "steam_playtest_invite",
"build_label": "playtest-invite-2026-05-24-rc3",
"f12_devtools": false,
"ingame_debug_menu": false,
"window_debug_globals": false,
"cheat_keybinds": false,
"verified_at": "2026-05-24"
},
{
"surface_id": "fest_public",
"build_label": "fest-demo-2026-10-rc1",
"f12_devtools": false,
"ingame_debug_menu": false,
"window_debug_globals": false,
"cheat_keybinds": false,
"verified_at": "2026-05-24"
}
]
}
Attach beside BUILD_RECEIPT with surface and scripting_defines_hash columns.
Step 5 — File isolation receipt before fest visibility
{
"schema": "playtest_isolation_receipt_v1",
"isolation_pass": true,
"gates": {
"I3_dev_menus_excluded": true,
"playtest_console_smoke": true,
"fest_public_unchanged": true
},
"notes": "Playtest rebuilt rc3; fest_public not re-uploaded"
}
Block fest visibility toggle if playtest row failed console smoke but fest is clean—players still compare notes in Discord.
Verification checklist
- [ ]
playtest_scope_map_v1.jsonlists separate depots per surface. - [ ]
dev_console_allowedmatches studio policy per row. - [ ] Installed playtest build: console keys do nothing (if policy false).
- [ ] Installed fest_public build: console keys do nothing.
- [ ]
dev_console_ship_receipt_v1.jsonallfalseon retail surfaces. - [ ] BUILD_RECEIPT define diff between surfaces documented.
- [ ] Wednesday smoke S7 run on both installs separately.
- [ ] Playtest feedback forms require
surface+build_label.
Prevention
- Two CI workflows —
upload-playtestvsupload-festwith different define sets. - Never promote playtest from
developthe same hour you freezefest_public. - Add menu FPS cap pass—uncapped menus plus console doubles “unfinished” signals.
- Tag VOD triage with
surfaceper Whisper playtest pipeline. - Producer signs isolation receipt before marketing spend on fest week.
Troubleshooting
| Symptom | Fix |
|---|---|
| Fest has console, playtest clean | Wrong depot on fest_public—re-upload retail fest build |
| Only invite broken | Rebuild playtest lane defines; check NW.js template |
| Console in editor only | Invalid test—use Steam-installed build |
| Intermittent F12 | Overlay mod or Steam overlay—test clean Windows user |
| Playtest needs console for QA | Move testers to beta_ffa row; keep invite retail |
Same build_label both surfaces |
Rename labels—support cannot triage |
FAQ
Should playtest ever allow console?
Only on internal / closed beta_ffa rows with NDAs—not on invite keys that sit beside fest marketing.
Is this the same as wrong branch save paths?
Parallel isolation failure—see GDevelop save-path help for storage; this article is debug surface separation.
Unity Development Build on playtest only?
Strip defines and rebuild playtest depot; fest_public manifest must reference a different artifact ID.
Construct NW.js F12?
Disable devtools in export for playtest template; see Construct freeze week.
Related links
- Playtest vs Fest Demo Isolation Playbook (2026 H2)
- Your Steam Demo Should Not Ship With Developer Console Enabled (opinion)
- 12 Free Steam Playtest Fest Demo Isolation Runbooks
- Wednesday Demo Build Smoke Ritual
- Fest Demos Should Cap Menu Frame Rate (opinion)
- Steamworks — Playtest documentation
- Steamworks — Branches
Prove console policy on the installed Playtest client and the installed fest demo—not in the editor, not from a zip on Discord.