Stop Shipping Fest Demos Without a Player-Visible Build Label - 2026 Opinion
A player posts in Discord: “Menu crash after the patch.” You ask for logs. They send a screenshot with no version string. You promoted three depots this week. Your BUILD_RECEIPT knows fest-demo-2026-10-rc6—the player’s binary does not.
October 2026 fest volume turns support into archaeology. Teams that file beautiful receipt JSON internally but ship silent UIs waste the entire observability stack: crash symbolicate evenings, refund correlation playbooks, and Deck log listicles all assume someone can say which build failed.
This Opinion & Hot Takes piece makes a narrow claim: every fest_public demo must show a player-visible build_label that matches BUILD_RECEIPT and your promotion log—not only Steam’s opaque buildid, not only CI job numbers, not only a wiki page players never open.
Non-repetition note: Developer console opinion covers debug surfaces; Ren'Py seven-day label freeze covers VN classification discipline; GX multi-channel resource covers HTML5 dual-upload receipts. This URL owns why the label must be visible to players on fest demos.
Who this opinion is for
| Audience | Why read |
|---|---|
| Solo founder answering Discord | Stop “which patch?” threads |
| Producer | Add label gate to Wednesday smoke |
| Engineer | Wire one string from build pipeline to UI |
| Community manager | Copy/paste reply template with build_label field |
Why this matters now (October 2026)
- Multi-promote fest weeks — Founder time-box worksheet assumes hotfix depots; without visible labels, support cannot keep pace.
- Playtest vs fest_public isolation — Playtest scope playbook fails when players cannot prove which surface they installed.
- Refund and crash correlation — Refund dashboard rows key on
build_label; player reports without labels become unmergeable noise. - Stream and clip culture — OBS overlays and highlight reels need readable labels; VO reel safe zones already treat
build_labelas a first-class overlay element. - Publisher diligence — Q4 telemetry expectations (planned sibling) assume players and partners can reference the same build string.
Direct answer: Show build_label in credits, title footer, or hold-to-view debug panel on fest_public; file visible_build_label_receipt_v1.json before promotion; fail smoke if UI string ≠ BUILD_RECEIPT.
The opinion in one paragraph
Retail fest discipline means the person playing your demo can answer “what build is this?” without opening Steam client properties, without joining a developer-only Discord, and without guessing from file dates. Internal-only build_id discipline is necessary but insufficient—if the label is not on screen (or behind one obvious, documented gesture), you have outsourced correlation to support volunteers during your highest-traffic week. That is not indie charm; it is operational debt you chose to ship.
What counts as “player-visible”
| Surface | Visible? | Fest opinion |
|---|---|---|
| Credits / About screen small text | Yes | Preferred default |
| Title screen corner (8–10 pt monospace) | Yes | Acceptable |
Hold F1 / Select debug panel |
Yes if documented in FAQ | Acceptable |
| Steam client → Properties → Builds | No | Player may not know path |
Only in Player.log |
No | Support-only |
| Only in BUILD_RECEIPT zip | No | Partner-only |
| Discord pin with version | No | Players do not read pins before crashing |
Minimum bar: A player following your fest FAQ can report build_label=fest-demo-2026-10-rc6 in one screenshot.
Beginner path — one evening label
| Step | Minutes | Action |
|---|---|---|
| 1 | 10 | Pick display surface (credits line) |
| 2 | 15 | Pipe BUILD_LABEL from same source as BUILD_RECEIPT |
| 3 | 10 | Cold launch → photograph label |
| 4 | 15 | File visible_build_label_receipt_v1.json |
| 5 | 10 | Add FAQ line: “Report build label from credits” |
Success check: A non-dev friend reads the label aloud from a photo you did not stage.
Developer path — gates V1–V6
| Gate | Pass criterion |
|---|---|
| V1 | Label rendered on fest_public cold install |
| V2 | UI string == BUILD_RECEIPT.build_label |
| V3 | UI string == upload_log.csv row for promote |
| V4 | Steam buildid (if queried) maps to same label in receipt |
| V5 | Privacy exceptions documented (no PII in label) |
| V6 | visible_build_label_receipt_v1.json committed |
Optional seventh check: label visible in Wednesday smoke screenshot attachment.
visible_build_label_receipt_v1.json
{
"schema": "visible_build_label_receipt_v1",
"build_label": "fest-demo-2026-10-rc6",
"surface": "fest_public",
"display": {
"location": "credits_footer",
"gesture_required": false,
"font_min_pt": 9,
"screenshot_path": "release-evidence/ops/visible-label-rc6.png"
},
"parity": {
"build_receipt_match": true,
"upload_log_match": true,
"steam_buildid": "21457823",
"steam_buildid_maps_to_label": true
},
"privacy": {
"contains_user_id": false,
"contains_machine_id": false,
"exceptions_documented": []
},
"gates": {
"V1_visible_on_cold_install": true,
"V2_matches_build_receipt": true,
"V3_matches_upload_log": true,
"V4_steam_buildid_mapped": true,
"V5_privacy_ok": true,
"V6_receipt_committed": true
},
"promotion_allowed": true
}
Store under release-evidence/ops/ beside BUILD_RECEIPT. Link path in optional BUILD_RECEIPT column visible_build_label_receipt_path.
Single source of truth (do not fork strings)
CI / local build
│
├─► generated_build_label.h (or .gd, .cs)
│ │
│ ├─► in-game UI Text
│ ├─► BUILD_RECEIPT.json
│ └─► upload_log.csv
│
└─► steamcmd / Steamworks promote notes (human copy)
Anti-pattern: Artist types version in credits while engineer updates JSON manually—classic rc5 UI with rc6 receipt.
Engine sketches (illustrative)
| Engine | Single-source pattern |
|---|---|
| Unity | Application.version from ProjectSettings + pre-build script stamp |
| Godot | ProjectSettings application/config/version + autoload BuildInfo |
| Unreal | BuildSettings + UGameInstance subsystem read |
| Ren'Py | config.version on title screen (pairs VN freeze challenge) |
| Construct / NW.js | version.txt fetched at boot into Text object |
| Bevy | env!("BUILD_LABEL") via build.rs |
Full engine chapters belong in guides; this opinion only demands one generator, many consumers.
Privacy exceptions (when not to show raw ids)
| Case | Acceptable label | Document in receipt |
|---|---|---|
| Kids’ title / COPPA-sensitive | Short fest-2026-10 code |
privacy.exceptions |
| Competitive anti-sniping | Hash suffix only fest-a8f3 |
FAQ explains lookup |
| Narrative fourth-wall break | Credits-only, no HUD | display.location |
| Streamer mode hides HUD | Hold-to-view panel | gesture_required: true |
Never put in visible label: Steam account id, email, hardware serial, playtest invite codes, internal employee names.
Multi-channel VERSION parity
Teams shipping Steam + itch + GX must show the same build_label on every fest_public channel or prefix channel explicitly:
| Channel | Label example | Rule |
|---|---|---|
| Steam fest demo | fest-demo-2026-10-rc6 |
Canonical |
| itch HTML5 | fest-demo-2026-10-rc6-web |
Suffix _web allowed if documented |
| GX.games | fest-demo-2026-10-rc6-gx |
Same |
Mismatch without suffix is worse than no label—players cross-compare channels and call fraud.
Pair with 14 GX multi-channel tools and itch WASM smoke tutorial.
Support workflow (Discord template)
Thanks for the report. Please send:
1) Screenshot of the build label (Credits → bottom line, or hold Select for Build Info)
2) Platform (Windows / Deck / Linux)
3) What happened in one sentence
Do not send save files until we confirm build_label.
Pin template during fest week. Train moderators: no triage without label.
Pairing with observability receipts
| Receipt | Requires visible label because |
|---|---|
| Crash symbolicate | Minidump folder names use build_label |
| Deck log bundle | Zip discipline |
| Refund correlation | CSV join key |
| Top-20 hub row 8b | Symbolicate proof |
Without V1–V2, downstream receipts become internal fiction.
vs developer console (complementary opinions)
| Topic | Dev console opinion | This opinion |
|---|---|---|
| F12 devtools | Disable on fest_public | Orthogonal |
| Debug overlay with cheats | Ban | Overlay may show label if cheats removed |
build_label in corner |
Not discussed | Require on fest_public |
You can show label in a non-cheating debug panel; you cannot hide label while leaving giveGold() callable.
vs menu FPS cap opinion
Menu FPS cap addresses hardware trust signals. Visible label addresses support correlation. Ship both—label text in corner does not excuse 400 FPS menus.
Wednesday smoke extension (producers)
Add to weekly smoke checklist:
- [ ] Screenshot includes readable
build_label - [ ] Label matches BUILD_RECEIPT row for candidate promote
- [ ]
visible_build_label_receipt_v1.jsonupdated orpromotion_allowed: false
Ten extra minutes beats forty-eight hours of Discord guessing.
Seven failure modes (composite patterns)
| ID | Pattern | Fix |
|---|---|---|
| F1 | Label only in internal QA branch | Promote receipt + UI together |
| F2 | 1.0.0 semantic version without fest id |
Use fest-demo-YYYY-MM-rcN |
| F3 | Label in editor overlay stripped in export | Verify installed build |
| F4 | Playtest build label on fest_public depot | Isolation checklist |
| F5 | Marketing trailer build ≠ demo build | Separate receipt rows |
| F6 | Localized credits hide ASCII label | Unicode-safe monospace |
| F7 | Receipt says rc6, UI says rc6-hotfix1 | Single generator |
Proof table (promote week)
| Artifact | Path |
|---|---|
| Visible label screenshot | release-evidence/ops/visible-label-*.png |
| Receipt JSON | visible_build_label_receipt_v1.json |
| BUILD_RECEIPT | same folder, matching build_label |
| Upload log row | upload_log.csv |
| Smoke log | wednesday-demo-smoke-*.md |
Working dev — jq parity check
LABEL_UI="fest-demo-2026-10-rc6"
LABEL_RECEIPT=$(jq -r .build_label release-evidence/ops/BUILD_RECEIPT.json)
test "$LABEL_UI" = "$LABEL_RECEIPT" && echo OK || echo MISMATCH
Run in CI after packaging step, before steamcmd.
Publisher one-liner
Fest_public builds display
build_labelmatching our BUILD_RECEIPT and upload log;visible_build_label_receipt_v1.jsonis in the evidence folder.
Attach screenshot. Pair with Q3 diligence packets when asked for ops maturity.
Hour theft audit (support leads)
| Support time sink | Root cause | Label fix |
|---|---|---|
| “Which patch?” loop | No visible label | V1 |
| Wrong minidump bucket | Player guessed version | V2 + symbolicate |
| Playtest vs demo confusion | Same marketing name | Surface prefix in label |
| Refund row orphan | Missing join key | V2 + correlation CSV |
Steamworks note (not a metadata checklist)
18 Steamworks resources cover depot promotion and SteamPipe—this opinion does not duplicate branch naming checklists. It adds: after promote succeeds, verify the installed binary shows the label you logged.
Counterarguments (and answers)
| Pushback | Response |
|---|---|
| “Players do not read credits” | They screenshot credits when asked—moderators need a target |
| “Spoilers / immersion” | Use short code, not changelog essay |
| “Competitors scrape builds” | Labels are not secret security; use non-guessable suffix if needed |
| “We track crashes in Sentry” | Sentry still needs release string—sync with visible label |
| “Only 50 players” | Fest spikes are non-linear; fifty angry threads without labels still hurt |
| “QA branch has label” | Players install fest_public, not QA |
Opinions are for tradeoffs you already made unconsciously—this one chooses support truth over aesthetic minimalism.
Localization and accessibility
| Concern | Practice |
|---|---|
| Translated credits | Keep build_label in Latin monospace on all locales |
| RTL layouts | Pin label to bottom-start corner with margin |
| Color blindness | Avoid green-on-green; white on 80% black bar |
| Font scaling | Test 125% OS scale; label must remain readable |
| Photosensitive | No blinking version text |
Accessibility is not optional for a string that prevents mis-triage.
CI wiring (working dev, minimal)
- Generate
build_label.txtin build job fromgit describe+ fest branch. - Embed into game via pre-build step (engine-specific).
- Copy same file into
BUILD_RECEIPT.jsongeneration script. - Assert post-build: parse packaged UI screenshot OCR optional; minimum: read exported
version.txtbeside binary. - Block promote if
visible_build_label_receipt_v1.jsonmissing from artifact upload.
Pair with GitHub Actions cache bust tutorial (planned backlog #13) for “green CI, wrong label” class failures.
Unity evening sketch (beginner)
- Create
Assets/Resources/BuildLabel.txtfrom CI. TextMeshProin credits readsResources.Load<TextAsset>("BuildLabel").text.- Export Windows demo; open
*_Datafolder—confirm file baked. - Add
visible_build_labelbool to BUILD_RECEIPT template.
Godot evening sketch (beginner)
- Autoload
BuildInfo.gdreadsres://build_label.txt. Labelnodevisible = trueon credits scene.- Export preset stamps file via
export_presets.cfghook or shell copy pre-export. - Run Godot save isolation preflight cousin—different surface, same receipt culture.
Ren'Py and VN teams
VN demos already show config.version on some title screens—opinion requires that string equal BUILD_RECEIPT and survive translation freeze. Do not rename config.version mid-fest without bumping receipt and UI in same promote. Full freeze discipline: seven-day Ren'Py challenge.
Construct / NW.js teams
Export version.txt next to .exe; load at boot into DOM Text object. Verify packaged path, not preview. Pair NW.js console opinion—label in DOM is fine; devtools are not.
Extended pattern — refund screenshot without label
Composite support arc (no invented metrics):
- Player refunds citing “crash after update”
- Refund row lacks
build_labeljoin - Team searches Steam comments for version hints
- Engineer finds three active depots
- Symbolicate run uses wrong symbols
- Hotfix ships to wrong branch
- Social post claims “lazy devs”
One visible string collapses steps 2–5 into a ten-minute correlation. That is why refund playbook assumes labels exist on both sides of the join.
Extended pattern — playtest leak
- Playtest invite build shares marketing name with fest demo
- Streamer installs wrong depot
- Bug report references feature not in fest scope
- Team reproduces on internal branch
- Fest demo “unfixed” narrative spreads
Prefix labels: playtest-… vs fest-demo-…. Isolation playbook plus visible label makes mistakes legible.
BUILD_RECEIPT template columns (optional)
Add to your template JSON schema comments:
"build_label": "fest-demo-2026-10-rc6",
"visible_build_label_receipt_path": "release-evidence/ops/visible_build_label_receipt_v1.json",
"visible_label_location": "credits_footer"
Document in top-20 hub row 8b cousin row if you maintain a living index table.
Facilitator README row (playtest ops)
Add to facilitator doc (pairs 18 playtest tools):
| Field | Value |
|---|---|
require_build_label_screenshot |
true |
playtest_form_field |
build_label (required) |
visible_label_faq_url |
this blog URL |
Async playtest without label is anecdote, not data.
Key takeaways
- Fest_public demos must show player-visible
build_label. - Label must match BUILD_RECEIPT and upload log (V2–V3).
- File
visible_build_label_receipt_v1.jsonbefore promote (V6). - Credits footer is the beginner-default surface.
- Privacy exceptions are allowed but must be documented (V5).
- Multi-channel labels need suffix discipline or exact parity.
- Discord template should require label screenshot first.
- Pairs crash symbolicate and refund correlation.
- Distinct from dev console and Ren'Py freeze challenge.
- Extend Wednesday smoke with label screenshot.
- Single generator for UI + receipts—never dual-entry.
- Steam
buildidmaps in receipt (V4) for engineer audits. - October 2026 multi-promote weeks make hidden labels unaffordable.
- Founder time-box assumes support hours—labels reduce waste.
promotion_allowed: falsewhen gates fail—do not hope players guess.
FAQ
Is Steam’s buildid enough?
No for player support—most players never open client build properties. Engineers may log buildid in receipt V4 in addition to visible label.
Semver in store page vs fest label?
Store semver can differ; fest label should encode fest branch identity (fest-demo-2026-10-rc6).
Hide label behind secret key combo?
Allowed only if FAQ documents gesture and moderators train players—default to credits visibility.
Does label replace BUILD_RECEIPT?
No—receipt is authoritative; UI is the player-facing mirror.
Anti-cheat concerns?
Use non-guessable suffix if needed; still visible, not security through obscurity.
HTML5 demos?
Show label on boot splash or pause menu; pair GX/itch receipts.
What if label overlaps HUD?
Move to credits—not an excuse to omit.
Internal playtest builds?
May use playtest- prefix in label; never promote playtest label to fest_public without changing string.
Streamer hides HUD?
Burn-in label in OBS setup or require pause-menu label—document in facilitator README.
Publisher wants only semver?
Provide semver and fest build_label in diligence zip README.
Can we rotate label nightly during fest?
Yes—each promote updates UI, receipt, and moderator pin in the same commit. Never rotate receipt without promote.
Month-before-fest adoption ladder
| Week | Action |
|---|---|
| T-4 | Add build_label to BUILD_RECEIPT template |
| T-3 | Implement single generator + credits UI |
| T-2 | First visible_build_label_receipt_v1.json on internal |
| T-1 | Train Discord mods with screenshot template |
| T0 | Verify label in Wednesday smoke attachment |
| T+3 | Retrospective: count threads missing label |
Evidence folder layout
release-evidence/
ops/
BUILD_RECEIPT.json
upload_log.csv
visible_build_label_receipt_v1.json
visible-label-rc6.png
fest-week-capacity-2026.md # optional, pairs time-box worksheet
Keep ops receipts together so Thursday row review finds label proof in one glance.
Moderator escalation tree
Player report arrives
│
├─ Screenshot shows build_label? ──NO──► Reply with template (ask credits photo)
│
YES
│
├─ Label matches current BUILD_RECEIPT promote candidate? ──NO──► Ask reinstall / verify branch
│
YES
│
└─ Route to engineering with label + platform + one-line repro
Escalation without label is deferred, not ignored—protects engineers from false repro on wrong depot.
Streamer and press kit note
Press kits should include where the label lives (“bottom-right credits, 9pt monospace”). Reviewers are players; they will not read your Notion ops doc. If you burn in label for capture, match in-game string exactly—highlight reel typography already warns against covering the label with waveform thumbs.
Relationship to planned backlog siblings
| Backlog row | How this opinion differs |
|---|---|
| #10 Q4 diligence telemetry | Partner-facing maturity narrative |
| #11 Whisper → GitHub issues | Requires build_id facts only—visible label supplies them |
| #12 Tuesday CSV ingest | CSV rows should include build_label column |
| #13 GitHub Actions cache bust | CI truth must surface in UI |
Shipping this opinion early makes later cluster rows cheaper—you stop debating whether players can name the build.
Conclusion
Observability without a visible build string is a filing cabinet players cannot open.
Show the label. Match the receipt. File visible_build_label_receipt_v1.json. Train Discord to ask once.
Next reads: BUILD_RECEIPT beginner pipeline, Crash symbolicate evening, Wednesday demo smoke, Top-20 evidence receipts.