Thursday BUILD_RECEIPT Row Review Ritual - Ten Minutes After Wednesday Smoke
Your Wednesday demo smoke log says GREEN. Marketing schedules branch promotion for Thursday lunch. Stable receipt filenames for each row live in the top 20 evidence receipts index—use the same names in build_receipt_row_diff_receipt_v1.json notes. At 3 p.m. you discover BUILD_RECEIPT.json still lists last week's build_id, surface still says internal, and channel_label_match is missing the itch HTML5 row you uploaded Tuesday. The binary was fine—the receipt lied.
May–October 2026 micro-studios fixed store copy with Wednesday metadata diff and fixed boot crashes with Wednesday smoke. The third failure mode is quieter: receipt row drift between what you tested and what partners, Steamworks, and your own upload log claim. This Process & Workflow article defines a ten-minute Thursday BUILD_RECEIPT row review—a diff of gate columns and scope rows, not another FAQ or descriptor checklist.
Non-repetition note: This URL owns receipt row diff after smoke. It does not replace BUILD_RECEIPT beginner tutorial (schema authoring), Wednesday metadata diff (store copy), or playtest isolation (scope policy)—it closes the loop between them.
Who this is for and what you get
| Audience | You will be able to… |
|---|---|
| Solo dev | Diff this week's receipt against last promoted row in ten minutes |
| Release owner | Block promotion when R4–R7 fail even if smoke was GREEN |
| Producer | Require build_receipt_row_diff_receipt_v1.json beside demo_smoke_receipt_v1.json |
Time: 10 minutes every Thursday (default), immediately after Wednesday smoke when both run the same week.
Prerequisites: BUILD_RECEIPT.json for current build_id, archived receipt from last promotion, Wednesday smoke log + demo_smoke_receipt_v1.json, and a one-page row catalog (below).
Why this matters now (May 2026)
- Multi-channel uploads — GX.games, itch HTML5, and Steam now appear in one receipt; triple-channel label help exists because teams forget rows—not because smoke failed.
- Receipt columns multiplied —
menu_fps_cap,loudness_receipt,string_table_receipt,wasm_mime_receipt, anddemo_smokegates ship in 2026 tutorials; a diff catches stale false faster than re-reading JSON by hand. July VO clusters addwav_consent_metadata_ok,metadata_survives_mux_ok, andffprobe_duration_matrix_ok—pair row diffs with consent scope and expiry governance so booleans are not GREEN on deprecatedconsent_scopevalues. After the H1 trust arc, add a Capstone block for Lessons 212–216 via Lesson 217: H1 player trust observability capstone (h1_player_trust_capstone_v1.json). After Q1 post-fest recovery, add Q1 Capstone for Lessons 224–228 via Lesson 229 (q1_post_fest_capstone_v1.json). - Playtest vs fest surfaces — Playtest isolation fails when
surfacein receipt still saysplaytest_inviteafter you promotedfest_public. - Partner diligence — Q3 evidence packets join receipt hash, smoke receipt, and upload log—row review is how you prove they describe one build.
- Refund dashboards — Refund signal spreadsheets tag
build_id; receipt drift makes tags meaningless.
Direct answer: Open BUILD_RECEIPT.json and last promoted receipt side by side; run gates R1–R10; write build_receipt_row_diff_receipt_v1.json; no promotion if R4–R7 are RED—even when Wednesday smoke was GREEN.
Three Wednesdays, one Thursday (ritual map)
| Day | Ritual | Layer | This article |
|---|---|---|---|
| Wednesday AM | Metadata diff | Store copy and media | Prerequisite—fix copy lies first |
| Wednesday PM | Demo smoke | Installed binary | Prerequisite—prove golden path |
| Thursday | BUILD_RECEIPT row review (this) | Machine-readable claims about that binary | Diff rows before promotion |
| Friday | Block 5 evidence hygiene | Folder + archive cleanup | Weekly—not a promotion gate |
Smoke GREEN with receipt RED still ships governance theater—partners see a receipt that does not match the build players install.
Row catalog (what to diff every week)
Store release-evidence/05-operations/receipt-row-catalog.md once; link from BUILD_RECEIPT template notes:
| Row key | Type | Must match smoke? | Typical drift |
|---|---|---|---|
build_id |
string | Yes | Forgot to bump after hotfix |
git.commit |
string | Yes | Smoke on commit A, receipt still B |
surface |
enum | Yes | playtest_invite vs fest_public |
channel / channels[] |
list | Yes | Added itch row, receipt still Steam-only |
channel_label_match |
bool | Yes | false when GX + Steam labels differ |
demo_smoke.promotion_allowed |
bool | Yes | true in log, false in receipt |
demo_smoke.iso_week |
string | Yes | Last week's W20 receipt |
menu_fps_cap.verified |
bool | If menu demo | Missing after menu FPS opinion |
loudness_receipt.master_ok |
bool | If trailer shipped | Stale after LUFS listicle pass |
string_table_receipt.tables_ok |
bool | If localized build | Empty tables after Addressables strip (help) |
wasm_mime_receipt.post_upload_mime_ok |
bool | If itch HTML5 | CDN MIME regression (help) |
playtest_scope_map.version |
string | If playtest surface | Scope map not bumped with branch |
human_signoff.signed_at_utc |
timestamp | Yes | Unsigned receipt promoted anyway |
Add rows when you adopt new JSON sidecars—never delete rows mid-fest without a receipt_version bump documented in the diff notes.
Gates R1–R10 (Thursday promotion blockers)
| Gate | Name | Pass criteria |
|---|---|---|
| R1 | File exists | Current BUILD_RECEIPT.json in repo + zip mirror path documented |
| R2 | build_id parity | Same string in receipt, smoke receipt, upload_log last row |
| R3 | git parity | git.commit matches machine that produced smoke build |
| R4 | surface parity | surface matches intended promotion target this week |
| R5 | channel rows | Every live channel has a row; no orphan uploads in upload_log.csv |
| R6 | channel_label_match | true when multi-channel; documented exception in notes if false |
| R7 | demo_smoke linkage | demo_smoke_receipt_v1.json attached path + promotion_allowed true |
| R8 | Gate columns | Required bool rows for this SKU (menu, loudness, wasm, string table) not stale vs last week |
| R9 | signoff | human_signoff.signed_at_utc within 7 days of smoke |
| R10 | diff receipt | build_receipt_row_diff_receipt_v1.json committed |
R4–R7 are promotion blockers when RED. R8 YELLOW allows promotion only with written waiver in diff notes (producer sign-off).
The ten-minute ritual (minute-by-minute)
| Min | Action |
|---|---|
| 0–1 | Confirm Wednesday smoke log GREEN; open current + receipts/{last_promoted}.json |
| 1–3 | R1–R3: build_id, commit, branch string equality |
| 3–5 | R4–R6: surface, channels, channel_label_match |
| 5–7 | R7: open demo_smoke_receipt_v1.json; verify promotion_allowed |
| 7–9 | R8–R9: bool gate columns vs catalog; signoff freshness |
| 9–10 | Write diff receipt; mark RED/YELLOW/GREEN; calendar "promotion allowed?" |
Calendar invite: "Thursday BUILD_RECEIPT row review — promotion gate (receipt)."
Same-week calendar (recommended)
| Day | Block | Total |
|---|---|---|
| Wednesday | Metadata diff + demo smoke | ~30 min |
| Thursday | Row review (this) | 10 min |
| Thursday PM | Branch promotion (human) | Only if Wed + Thu GREEN |
build_receipt_row_diff_receipt_v1.json
{
"schema": "build_receipt_row_diff_receipt_v1",
"iso_week": "2026-W21",
"reviewed_at_utc": "2026-05-22T09:12:00Z",
"current_build_id": "nextfest-oct-2026-rc4",
"baseline_build_id": "nextfest-oct-2026-rc3",
"paired_demo_smoke": "release-evidence/05-operations/demo-smoke/demo_smoke_receipt_v1-rc4.json",
"gates": {
"R1_file_exists": "pass",
"R2_build_id_parity": "pass",
"R3_git_parity": "pass",
"R4_surface_parity": "pass",
"R5_channel_rows": "pass",
"R6_channel_label_match": "pass",
"R7_demo_smoke_linkage": "pass",
"R8_gate_columns": "pass",
"R9_signoff": "pass",
"R10_diff_receipt": "pass"
},
"row_diff": [
{
"key": "surface",
"baseline": "playtest_invite",
"current": "fest_public",
"expected": "fest_public",
"status": "fixed"
},
{
"key": "channel_label_match",
"baseline": false,
"current": true,
"expected": true,
"status": "fixed"
}
],
"promotion_allowed": true,
"notes": "Promoted after rc3 playtest; rc4 adds itch HTML5 channel row"
}
Commit under release-evidence/05-operations/receipt-diffs/. Validate-packet can assert promotion_allowed and row_diff[].status != unexpected.
Diff mechanics (beginner-friendly)
You do not need a custom tool on day one—jq or a ten-line Python script is enough.
jq one-liners (illustrative)
jq -r '.build_id, .surface, .gates.demo_smoke.promotion_allowed' BUILD_RECEIPT.json
jq -r '.build_id, .surface' release-evidence/05-operations/receipts/nextfest-oct-2026-rc3.json
diff <(jq -S .gates release-evidence/05-operations/receipts/nextfest-oct-2026-rc3.json) \
<(jq -S .gates BUILD_RECEIPT.json)
Python sketch (row catalog driven)
import json
from pathlib import Path
CATALOG = ["build_id", "surface", "channel_label_match",
"demo_smoke.promotion_allowed", "menu_fps_cap.verified"]
def get(obj, dotted):
for part in dotted.split("."):
obj = obj.get(part, {}) if isinstance(obj, dict) else None
return obj
cur = json.loads(Path("BUILD_RECEIPT.json").read_text())
base = json.loads(Path("receipts/baseline.json").read_text())
for key in CATALOG:
b, c = get(base, key), get(cur, key)
if b != c:
print(key, b, "->", c)
Run in CI after smoke job uploads artifacts—fail build if R4–R7 would be RED.
Worked example (composite duo, fest week)
Context: nextfest-oct-2026-rc4 smoke GREEN Wednesday; Thursday review finds receipt drift.
| Gate | Finding | Status |
|---|---|---|
| R2 | Smoke receipt rc4, BUILD_RECEIPT still rc3 |
RED |
| R4 | surface still playtest_invite |
RED |
| R6 | channel_label_match false; itch uploaded Monday |
RED |
| R7 | Smoke promotion_allowed true but receipt lacks demo_smoke block |
RED |
Outcome: Promotion slipped one day. Engineer bumped build_id, set fest_public, added itch channel row + channel_label_match: true, copied smoke JSON path into receipt, re-ran R1–R10—all GREEN. Marketing promoted Thursday PM instead of lunch.
Lesson: Smoke caught gameplay; Thursday caught paperwork that would have broken playtest isolation analytics and GX multi-channel resources.
Pairing with specialized receipts (2026 stack)
| Sidecar | Receipt row | Review focus |
|---|---|---|
demo_smoke_receipt_v1.json |
demo_smoke.* |
promotion_allowed, iso_week, build_id |
menu_fps_cap_receipt_v1.json |
menu_fps_cap.verified |
Deck menu power (opinion) |
loudness_receipt_v1.json |
loudness_receipt.master_ok |
Trailer/master chain (14 tools) |
playtest_vod_triage_receipt_v1.json |
playtest_vod.last_run |
Optional; proves triage ran (Whisper pipeline) |
string_table_receipt_v1.json |
string_table_receipt.tables_ok |
Localization fest builds |
wasm_mime_receipt_v1.json |
wasm_mime_receipt.post_upload_mime_ok |
itch HTML5 CDN |
Thursday review asks: did the bool in BUILD_RECEIPT change when the sidecar changed? If sidecar updated Tuesday and receipt bool still false Wednesday, R8 is RED.
Engine and SKU notes (row expectations)
Steam-only NW.js demo
Minimum rows: build_id, surface, demo_smoke, channel_label_match (single channel true), S7 console row in smoke notes.
Steam + itch HTML5
Require channels[] length ≥ 2, channel_label_match true, wasm_mime_receipt row when itch row present. Cross-check itch MIME help.
Steam + GX.games
build_label / VERSION alignment per GX receipt help; triple-channel cases use triple storefront help.
Localized Unity fest build
Add string_table_receipt and Weblate freeze cross-links (localization resource); R8 fails if store shows CN but tables_ok false.
upload_log.csv cross-check (R5)
Last three upload_log.csv rows must map to channels[] or explicit notes:
uploaded_at_utc,build_id,portal,zip_filename,portal_ticket,result,notes
2026-05-21T18:00:00Z,nextfest-oct-2026-rc4,steam_depot,rc4.zip,DEP-991,submitted,
2026-05-21T19:10:00Z,nextfest-oct-2026-rc4,itch_html5,rc4-html5.zip,,submitted,
2026-05-21T20:05:00Z,nextfest-oct-2026-rc4,gx_games,rc4-gx.zip,,submitted,
Orphan portal upload without receipt channel row → R5 RED.
RED / YELLOW / GREEN (Thursday)
| Status | Meaning | Promotion |
|---|---|---|
| RED | R4–R7 fail; build_id mismatch; unsigned receipt | No |
| YELLOW | R8 optional gate stale with documented waiver | No until waiver in diff notes |
| GREEN | R1–R10 pass; row_diff only fixed or unchanged |
Yes if Wednesday rituals also GREEN |
Log template (markdown)
# Thursday BUILD_RECEIPT row review — 2026-W21
current_build_id: nextfest-oct-2026-rc4
baseline_build_id: nextfest-oct-2026-rc3
reviewer: release_owner
| gate | status | notes |
|------|--------|-------|
| R1 | GREEN | |
| R2 | GREEN | |
| R3 | GREEN | |
| R4 | GREEN | surface fest_public |
| R5 | GREEN | steam + itch + gx |
| R6 | GREEN | channel_label_match true |
| R7 | GREEN | smoke receipt linked |
| R8 | GREEN | menu_fps + loudness ok |
| R9 | GREEN | signoff 2026-05-21 |
| R10 | GREEN | diff json committed |
Promotion this week: ALLOWED (receipt)
Save beside smoke logs under release-evidence/05-operations/receipt-diffs/.
Automation hooks (when you are ready)
| Hook | Trigger | Action |
|---|---|---|
| CI post-smoke | demo_smoke_receipt_v1.json uploaded |
Fail if BUILD_RECEIPT.build_id differs |
| Pre-promote script | Human runs promote | Require build_receipt_row_diff.promotion_allowed |
| Friday Block 5 | Weekly hygiene | Archive diff + smoke + metadata logs together |
Start manual ten minutes; automate only after two GREEN fest weeks—otherwise you script the wrong rows.
Starter validate snippet (optional)
Add to your existing validate-packet folder as validate-receipt-row-diff.sh:
#!/usr/bin/env bash
set -euo pipefail
DIFF="release-evidence/05-operations/receipt-diffs/build_receipt_row_diff_receipt_v1.json"
test -f "$DIFF" || { echo "missing diff receipt"; exit 1; }
jq -e '.promotion_allowed == true' "$DIFF" >/dev/null
jq -e '[.row_diff[] | select(.status == "unexpected")] | length == 0' "$DIFF" >/dev/null
echo "receipt row diff OK"
This does not replace human judgment on ambiguous UX gates—it only catches forgotten files and hard false promotion flags.
Distributed team handoff
| Role | Thursday duty |
|---|---|
| Engineer (EU) | Run R1–R3 before standup |
| Producer (US) | R4–R6 surface + channels |
| Either | R7–R10 + commit diff receipt |
Leave notes in diff JSON when Asia overnight smoke used a different Steam account—Asia-EU continuity depends on readable diffs, not Slack threads.
Evidence folder layout (one-time setup)
Create once under release-evidence/05-operations/:
05-operations/
BUILD_RECEIPT.template.json
upload_log.csv
receipt-row-catalog.md
demo-smoke/
wednesday-demo-smoke-YYYY-WW.md
demo_smoke_receipt_v1-{build_id}.json
receipt-diffs/
thursday-row-review-YYYY-WW.md
build_receipt_row_diff_receipt_v1-{build_id}.json
receipts/
{archived_build_id}.json
store-metadata/
wednesday-metadata-diff-YYYY-WW.md
Naming rule: ISO week in markdown logs; build_id in JSON filenames. Friday Block 5 archives anything older than eight weeks into archive/2026-Q2/—do not delete diff receipts that partners still reference.
When row review fails mid-promotion (rollback script)
If promotion already happened and Thursday review finds RED:
- Stop further marketing sends and Discord announcements.
- Document incident row in refund dashboard with tag
receipt_drift. - Roll back branch to last known-good
build_idper branch naming checklist. - Re-run Wednesday smoke on rolled-back depot.
- Fix receipt rows; re-run Thursday; attach both diff receipts (
rc4_bad,rc3_restored) for partner transparency.
This is rare when Thursday runs before promotion—it is expensive when skipped.
Receipt version bumps (schema discipline)
When you add a new gate column mid-fest:
"receipt_version": "1.1",
"changelog": [
{ "version": "1.1", "date": "2026-05-25", "added": ["wasm_mime_receipt.post_upload_mime_ok"] }
]
Thursday R8 compares only keys present in both baseline and current when versions differ—treat missing baseline keys as introduced in row_diff, not unexpected. Document in receipt-row-catalog.md the same day you ship schema 1.1.
Micro-studio time budget (realistic)
| Team size | Weekly ops total | Thursday share |
|---|---|---|
| Solo | 40 min (metadata + smoke + row) | 10 min |
| Duo | 35 min split | 5 min each |
| Contractor QA | +15 min spot-check | Contractor runs R7–R8 only |
Cheaper than one store-demo mismatch recovery day.
Pairing with October localization week
When Weblate freeze and store-page QA refresh land the same week, Thursday must include string_table_receipt.tables_ok and store_copy_receipt rows if your BUILD_RECEIPT template was extended—metadata diff alone will not catch in-game English-only selector bugs (Ren'Py language help when published).
Audit questions partners ask (answer from diff receipt)
| Partner question | Row / file that answers |
|---|---|
| Which build did you smoke-test? | R2 + demo_smoke_receipt_v1.json |
| Is this the public fest depot or playtest? | R4 surface |
| Did all storefront uploads share labels? | R6 channel_label_match |
| Who signed release this week? | R9 human_signoff |
| What changed since last submission? | row_diff[] in diff receipt |
Keep answers in README bullets—do not send raw JSON unless requested.
Common mistakes (beginners)
| Mistake | Fix |
|---|---|
| Diffing store FAQ text | That is Wednesday metadata, not this ritual |
| Promoting on smoke alone | Add Thursday row review |
| Copy-paste entire old receipt | Use row_diff; bump only changed keys |
| Ignoring itch row | R5 fails when upload_log has orphan portal |
| Skipping signoff | R9 RED—partners treat unsigned as incomplete |
Promotion checklist (append to runbook)
[ ] Wednesday metadata diff GREEN (same ISO week)
[ ] Wednesday demo smoke GREEN (same ISO week)
[ ] Thursday BUILD_RECEIPT row review GREEN (this article)
[ ] build_receipt_row_diff_receipt_v1.json promotion_allowed == true
[ ] upload_log.csv last row build_id matches receipt
[ ] No RED rows in row_diff array
Outbound references
- Steamworks Builds documentation — branch names must match receipt
branchnotes - JSON Schema draft 2020-12 — optional validation for diff receipt in CI
Related GamineAI reads
- BUILD_RECEIPT beginner pipeline
- Wednesday demo smoke
- Wednesday metadata diff
- Playtest isolation playbook
- Local Whisper VOD triage
- 18 playtest async tools
- Refund signal dashboard
- Friday Block 5
- BUILD_RECEIPT tutorial
Key takeaways
- Thursday row review diffs BUILD_RECEIPT gate rows against last promoted receipt—not store metadata.
- Run it after Wednesday smoke when both happen the same week.
- Gates R4–R7 (
surface, channels,channel_label_match, smoke linkage) are promotion blockers. - Ship
build_receipt_row_diff_receipt_v1.jsonwith arow_diff[]array documenting every changed key. - Cross-check
upload_log.csvso no portal upload lacks a receipt channel row. - Pair specialized sidecars (menu FPS, loudness, wasm MIME, string tables) with matching receipt bools.
- Ten minutes weekly beats promoting a GREEN binary with a RED receipt.
- Automate with jq or Python only after manual discipline works two fest weeks.
- Completes the trio: metadata Wednesday AM, smoke Wednesday PM, receipt Thursday.
- Fulfills forward links from playtest isolation and multi-channel resource briefs.
FAQ
Is this the same as Wednesday metadata diff?
No. Metadata diff compares store page copy and media to git-approved sources. Thursday review compares BUILD_RECEIPT.json rows to last promoted receipt and Wednesday smoke—binary claims, not FAQ text.
Can I promote if smoke was RED but I fixed receipt rows?
No. Receipt review assumes smoke GREEN. Fix binary first, re-smoke, then run R1–R10.
How is this different from SHA256 cold validation?
Cold validation proves file bytes. Row review proves scope and gate columns inside the receipt describe the build you hashed and smoked.
Do I need all sidecar JSON files every week?
Only the sidecars your SKU uses this week—see row catalog. Missing optional sidecar is fine; missing required bool for an active channel is R8 RED.
What if we only promote every two weeks?
Run row review on promotion weeks; on off weeks, still archive a diff against baseline so Friday Block 5 does not find silent drift.
Where does Blog-Planner go after this post?
Active Blog Backlog hits zero—schedule Blog-Planner refill before the next go expects another non-escape-hatch pitch.
Can partners see build_receipt_row_diff_receipt_v1.json?
Usually internal under release-evidence/05-operations/. Summarize pass/fail in partner README bullet linking BUILD_RECEIPT—same pattern as partner ZIP naming.
What surface values should fest demos use?
Align with playtest isolation—fest_public on the depot players install from the fest library slot, not playtest_invite or internal unless documented.