Beginner-Friendly Tutorials Jul 15, 2026

Your First itch.io + Steam Dual-Depot Dress Rehearsal Before October Upload Peak - One Evening - 2026

One-evening itch+Steam dual-depot dress rehearsal for October 2026—channel matrix, build_id proof, channel_label_match, smoke per surface, and dual_sku_rehearsal_ok on BUILD_RECEIPT.

By GamineAI Team

Your First itch.io + Steam Dual-Depot Dress Rehearsal Before October Upload Peak - One Evening - 2026

Pixel-art hero for itch.io and Steam dual-depot dress rehearsal before October upload peak

September 2026 is when micro-studios drip an itch.io HTML5 or browser demo while Steam Next Fest upload queues tighten. Marketing posts the itch “play now” link on Tuesday. Steam still serves last week’s depot. Your BUILD_RECEIPT.json lists one build_id in the header but the itch row still says 2026-09-04-rc01 while Steam shows 2026-09-11-rc02. Thursday promotion looks GREEN on smoke logs—and RED on wishlist math because players tried two different builds on two storefronts.

This tutorial is the dress rehearsal before that week. Not economics (see the itch PWYW vs Steam demo worksheet). Not recovery after wishlists split (forward blog #5 case study). Not Thursday’s ten-minute row diff ritual (that consumes this receipt). This URL owns the one-evening proof that itch and Steam channels agree on build_id, build_label, and channel_label_match before October upload peak.

Direct answer: spend one evening creating dual_sku_dress_rehearsal_receipt_v1.json. Run gates D1–D6. Document a dual-channel matrix, pin the same build_id on both surfaces, smoke itch browser + Steam client, set BUILD_RECEIPT.dual_sku_rehearsal_ok: true only when every gate passes.

Pair early with BUILD_RECEIPT beginner pipeline if receipts are new, and with localization string freeze evening when fest locales must match both storefronts.

Why this matters now

Dual-SKU distribution is default for 2026 PC indies, but ops evidence still lags.

  1. itch “instant play” hides version drift. Browser caches and Butler uploads can look successful while players fetch an older WASM bundle—see itch WASM MIME help when MIME is right but bytes are stale.
  2. Steam depot IDs are not your build_id. Steamworks build numbers and your receipt build_id must crosswalk explicitly or Thursday row review fails R6.
  3. October upload peak punishes late discovery. Finding channel mismatch during queue week costs a promotion day you cannot buy back.
  4. channel_label_match is a promotion latch. Thursday BUILD_RECEIPT row review treats false channel_label_match as RED—this rehearsal prevents that surprise.
  5. One evening is enough when scope stays narrow. One candidate build, two channels, one matrix, two smokes, one JSON receipt.

Official references: Steamworks Builds documentation, itch.io Butler documentation, and your engine export docs for HTML5 vs standalone targets.

Who this one-evening ritual is for

Reader What you should leave with
First-time dual-SKU team A channel matrix you can re-run before every fest promotion
Solo developer Proof that itch and Steam reference the same build_id
Release producer D1–D6 decision table with fail actions before upload week
Marketing lead Honest answer when asked “is the itch demo the same build as Steam?”
Build engineer Crosswalk table linking depot id, Butler version, and receipt rows

Time: 90–120 minutes first pass; 40–55 minutes once scripts and matrix template exist.

Prerequisites:

  • Live or staging itch page with HTML5 or downloadable demo lane.
  • Steam app with fest demo branch or playtest depot configured.
  • Existing BUILD_RECEIPT.json habit (beginner pipeline).
  • Candidate build exported for both targets (or documented why surfaces differ with exception ticket).
  • Browser for itch smoke; Steam client for depot smoke.
  • Optional: localization_freeze_ok from freeze evening when both SKUs claim the same locales.

Format ladder - where this article fits

Format Job Boundary
This tutorial One-evening dual-depot dress rehearsal with D1–D6 Owns dual_sku_rehearsal_ok
BUILD_RECEIPT beginner Root receipt + upload log habit Owns first receipt schema
Thursday row review Weekly promotion diff R1–R10 Consumes channel_label_match from this rehearsal
Wednesday demo smoke Binary health before promotion Owns smoke gates, not channel matrix
itch PWYW economics Runway and cost lanes Owns money math, not depot proof
Forward #5 case study Recover wishlist split after itch diluted Steam Owns recovery R1–R6, not prevention
Guide #50 / Help #55 / Resource #53 One-step fixes and tool lists Link here once published

Non-repetition note: Thursday row review owns weekly R-gates and build_receipt_row_diff_receipt_v1.json. Economics worksheet owns subsidy and runway. Localization freeze owns L1–L6. This URL owns pre-October dual-channel dress rehearsal with D1–D6 and dual_sku_dress_rehearsal_ok.

Gates D1–D6

Gate Question Required evidence Fail action
D1 Is one candidate build_id declared for this rehearsal? Single build_id string in receipt + git commit crosswalk Stop. Pick one candidate; never rehearse two builds silently.
D2 Are both channels documented in channels[]? itch row + Steam row with surface, URL, upload UTC Add missing channel or document defer with exception.
D3 Does channel_label_match hold across channels? Same build_label / VERSION token on itch page, in-game HUD, Steam description notes Align labels or file numbered exception in receipt notes.
D4 Can you prove each channel serves the candidate bytes? itch etag or Butler version + Steam buildid or manifest hash Re-upload stale channel; purge CDN if needed.
D5 Does smoke pass on itch browser and Steam client? Screenshots + short logs per surface Fix blocking bug; do not set dual_sku_rehearsal_ok.
D6 Is evidence filed and BUILD_RECEIPT updated? dual_sku_dress_rehearsal_receipt_v1.json + dual_sku_rehearsal_ok: true Block October promotion until complete.

Gates are PASS, FAIL, or NOT_RUN only.

Evening overview - four blocks

Block Minutes Output
1 — Declare candidate build_id 15 build_id + git commit in working notes
2 — Author channel matrix 25 channels[] with itch + Steam rows
3 — Label match + byte proof 30 D3 table + D4 hashes / buildids
4 — Dual smoke + receipt 40–50 D5 artifacts + dual_sku_rehearsal_ok

Evidence folder:

release-evidence/
  dual-sku/
    2026-09-12-october-rehearsal/
      dual_sku_dress_rehearsal_receipt_v1.json
      channel_matrix.md
      smoke/
        itch-main-menu.png
        steam-main-menu.png
        itch-smoke.log
        steam-smoke.log

Block 1 - declare one candidate build_id (D1)

Pick the build you intend to promote during October prep—not “latest main” and not “whatever itch cached.”

{
  "build_id": "studio_game_fest_2026-09-12_a1b2c3d4",
  "git": {
    "commit": "a1b2c3d4e5f678901234567890abcdef12345678",
    "branch": "fest/october-demo",
    "dirty": false
  },
  "rehearsal_utc": "2026-09-12T17:00:00Z"
}

Naming should follow the same discipline as partner ZIP naming: studio, game, channel context, short hash, UTC date.

Beginner rule: if itch and Steam were built from different commits tonight, you are not ready for D1 PASS—either rebuild both from one commit or document a numbered exception with expiry (still FAIL for promotion until resolved).

D1 passes when exactly one build_id owns the rehearsal.

Block 2 - channel matrix (D2)

Document both surfaces in channels[]—not a vague “we are on itch and Steam.”

{
  "channels": [
    {
      "surface": "itch_html5",
      "url": "https://your-name.itch.io/your-game",
      "play_url": "https://your-name.itch.io/your-game/html5/index.html",
      "upload_tool": "butler",
      "butler_version": "15.21.0",
      "uploaded_utc": "2026-09-12T16:10:00Z",
      "build_label": "fest_2026-09-12_a1b2c3d4"
    },
    {
      "surface": "steam_depot",
      "app_id": "1234560",
      "depot_id": "1234561",
      "branch": "fest_public",
      "steam_build_id": "9876543210",
      "uploaded_utc": "2026-09-12T16:45:00Z",
      "build_label": "fest_2026-09-12_a1b2c3d4"
    }
  ]
}

Add a human-readable channel_matrix.md for producers:

Surface Player entry Owner Last upload UTC
itch HTML5 itch page → Play in browser @build 2026-09-12T16:10:00Z
Steam demo Store → Download demo @release 2026-09-12T16:45:00Z

Append two rows to upload_log.csv per BUILD_RECEIPT beginner—one per portal, same build_id column.

D2 passes when both fest-facing channels exist with URLs, upload timestamps, and owners.

Block 3 - channel_label_match and byte proof (D3 + D4)

D3 - labels players and receipts can see

channel_label_match means the same build_label token appears:

  • On the itch project page or in-browser VERSION file (if you ship one)
  • In-game on a player-visible label per visible build label opinion
  • In Steam fest branch notes or demo description footnote your team actually maintains
{
  "channel_label_match": true,
  "build_label": "fest_2026-09-12_a1b2c3d4",
  "label_locations": [
    "itch: project devlog footer",
    "in_game: title screen lower-left",
    "steam: fest branch README in depot notes"
  ]
}

If labels intentionally differ (e.g., itch HTML5 omits HUD label), set channel_label_match: false and add label_exception_id: EXC-001 with owner and expiry—Thursday row review will stay RED until resolved.

D4 - prove bytes, not just labels

itch HTML5

curl -sI "https://your-name.itch.io/your-game/html5/game.wasm" | grep -i etag

Record etag, last-modified, and optionally SHA-256 of downloaded game.wasm after hard refresh (Ctrl+Shift+R). Stale CDN edges are a dress-rehearsal failure, not a surprise on fest day—see itch WASM MIME cache help.

Steam depot

Record steam_build_id from Steamworks → Builds for branch fest_public. Crosswalk to your zip SHA-256 from ServerData or standalone export folder.

{
  "byte_proof": {
    "itch": {
      "wasm_url": "https://your-name.itch.io/your-game/html5/game.wasm",
      "etag": "\"abc123\"",
      "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
    },
    "steam": {
      "steam_build_id": "9876543210",
      "primary_zip_sha256": "c0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1"
    }
  }
}

D3 passes when channel_label_match is true or a documented exception exists. D4 passes when both proofs reference the candidate tied to D1’s build_id.

Block 4 - dual smoke and BUILD_RECEIPT (D5 + D6)

D5 - smoke both surfaces

Minimum matrix:

Check itch browser Steam client
Reach main menu / first playable state required required
Visible build_label matches D3 required required
No blocking console error on boot required required
Screenshot filed required required

itch smoke log snippet:

[surface] itch_html5
[build_label] fest_2026-09-12_a1b2c3d4
[wasm_etag] "abc123"
[result] main_menu_ok

Steam smoke log snippet:

[surface] steam_depot fest_public
[steam_build_id] 9876543210
[build_label] fest_2026-09-12_a1b2c3d4
[result] main_menu_ok

If you run Wednesday demo smoke the same week, link its JSON path in the receipt—dress rehearsal proves channels agree; Wednesday proves binary health.

D6 - file receipt and latch BUILD_RECEIPT

{
  "schema": "dual_sku_dress_rehearsal_receipt_v1",
  "build_id": "studio_game_fest_2026-09-12_a1b2c3d4",
  "rehearsal_utc": "2026-09-12T19:30:00Z",
  "channels": [],
  "channel_label_match": true,
  "byte_proof": {},
  "smoke_dir": "release-evidence/dual-sku/2026-09-12-october-rehearsal/smoke",
  "gates": {
    "D1": "PASS",
    "D2": "PASS",
    "D3": "PASS",
    "D4": "PASS",
    "D5": "PASS",
    "D6": "PASS"
  },
  "dual_sku_rehearsal_ok": true,
  "localization_freeze_receipt": "release-evidence/localization/2026-09-12-october-fest-freeze/localization_freeze_receipt_v1.json",
  "notes": "Forward blog #5 if wishlist split detected post-launch."
}

Crosswalk root receipt:

{
  "dual_sku_rehearsal_ok": true,
  "dual_sku_dress_rehearsal_receipt": "release-evidence/dual-sku/2026-09-12-october-rehearsal/dual_sku_dress_rehearsal_receipt_v1.json",
  "channel_label_match": true,
  "channels": [
    { "surface": "itch_html5", "build_label": "fest_2026-09-12_a1b2c3d4" },
    { "surface": "steam_depot", "build_label": "fest_2026-09-12_a1b2c3d4" }
  ]
}

D6 passes when the receipt is on disk, all gates are PASS, and BUILD_RECEIPT pointers resolve.

verify_dual_sku_dress_rehearsal.sh sketch

#!/usr/bin/env bash
set -euo pipefail
RECEIPT="${1:-release-evidence/dual-sku/latest/dual_sku_dress_rehearsal_receipt_v1.json}"

test "$(jq -r .dual_sku_rehearsal_ok "$RECEIPT")" = "true"
test "$(jq -r .channel_label_match "$RECEIPT")" = "true"
for g in D1 D2 D3 D4 D5 D6; do
  test "$(jq -r ".gates.$g" "$RECEIPT")" = "PASS"
done
channels=$(jq '.channels | length' "$RECEIPT")
test "$channels" -ge 2
echo "dual_sku_rehearsal_ok"

Proof table

Artifact Proves Does not prove
build_id One candidate owns the rehearsal Wishlist conversion
channels[] Both surfaces documented Marketing copy quality
channel_label_match Labels agree across surfaces Identical binaries if scopes differ by design
Byte proof rows Stale CDN / wrong depot caught Long-term drift after rehearsal
Dual smoke Player-visible boot on both Every gameplay path
dual_sku_rehearsal_ok Promotion may proceed to Thursday diff Economics viability

Scenarios A–G

A - itch updated Monday, Steam still on last week

Symptom: D4 steam build_id older than git commit.
Action: FAIL D4; upload Steam before setting rehearsal ok.

B - Different build_label on itch footer vs HUD

Symptom: D3 false while marketers edited footer only.
Action: Align labels or fail promotion until EXC ticket resolves.

C - HTML5 scope smaller than Steam demo

Symptom: Legitimate content difference.
Action: Document scope table in receipt notes; keep channel_label_match true for version token, not feature parity claim.

D - WASM etag unchanged after Butler push

Symptom: itch smoke passes locally but etag stale.
Action: FAIL D4; follow MIME/cache help; hard-refresh smoke.

E - Steam branch typo fest_pubic

Symptom: Smoke hits wrong branch.
Action: FAIL D5; fix branch name in matrix and Steamworks.

F - No BUILD_RECEIPT yet

Symptom: D6 cannot latch.
Action: Run beginner pipeline first.

G - Wishlists drop after itch push

Symptom: Rehearsal was green; funnel split post-launch.
Action: Forward to blog #5 recovery case study—not a rehearsal pass issue.

Common mistakes

  • Rehearsing two build_id values without documenting scope split.
  • Checking itch only in editor export preview, not public URL.
  • Treating Steam buildid as optional metadata.
  • Setting channel_label_match: true when only Steam has a visible label.
  • Skipping upload_log.csv second row for itch.
  • Running dress rehearsal only once in August and assuming October still matches.
  • Confusing this with dual-SKU economics—money vs bytes.
  • Promoting on Thursday without linking rehearsal receipt path in row diff notes.

Troubleshooting

“Butler says success but players see old build.” Inspect etag and WASM SHA-256; purge browser cache; verify Butler pushed to the same itch channel you document in D2.

“Steam buildid does not match our zip hash.” Confirm you uploaded the zip tied to D1’s commit, not an older artifact from Downloads.

“itch is HTML5, Steam is Win64—must hashes match?” No. D4 proves each channel serves its candidate artifact for the same build_id intent. Document scope in notes.

“We only ship Steam for fest.” This rehearsal is optional—but if itch page still links “play in browser,” either remove the link or include itch in D2.

“GX.games is our third channel.” Extend channels[]; see triple-channel label match help when three HTML5 surfaces exist.

Printable checklist card (pin in release-evidence/dual-sku/)

[ ] D1 — One build_id + git commit recorded for tonight's candidate
[ ] D2 — channels[] has itch_html5 row AND steam_depot row with URLs
[ ] D2 — upload_log.csv has TWO rows (itch + Steam) sharing build_id
[ ] D3 — build_label identical on itch page, in-game HUD, Steam notes
[ ] D3 — channel_label_match true OR numbered EXC ticket with expiry
[ ] D4 — itch wasm etag / sha256 captured after hard refresh
[ ] D4 — steam_build_id matches Steamworks fest_public branch
[ ] D5 — itch browser smoke screenshot saved to smoke/
[ ] D5 — Steam client smoke screenshot saved to smoke/
[ ] D5 — Both smokes show same build_label token
[ ] D6 — dual_sku_dress_rehearsal_receipt_v1.json filed on disk
[ ] D6 — BUILD_RECEIPT.dual_sku_rehearsal_ok set only when D1–D6 PASS
[ ] Optional — localization_freeze_receipt path linked when locales claimed

Read the card aloud once before telling marketing the itch link is safe. Verbalizing catches “Steam uploaded yesterday, itch still loading” faster than assuming Butler green means players agree.

Crosswalk to Thursday row review (R6)

When Thursday row review runs the same week, copy these fields into build_receipt_row_diff_receipt_v1.json notes so R6 does not re-invent the matrix:

Dress rehearsal field Thursday R-gate
channel_label_match R6
channels[] length ≥ 2 R5 / R6
dual_sku_dress_rehearsal_receipt path R10 evidence linkage
build_id R2 / R3 diff anchor

If Thursday diff shows channel_label_match flipped from true to false after a mid-week itch-only upload, treat rehearsal as stale—re-run D1–D6 before promotion even if Wednesday smoke stayed GREEN.

Schedule pairing for September 2026

When Pair with
After localization freeze Freeze evening locales on both SKUs
Same week as promotion Wednesday smoke then Thursday row review
Before marketing spend itch PWYW economics
If funnel splits post-launch Forward blog #5 wishlist recovery case study

Key takeaways

  1. Dual-SKU fest prep needs a dress rehearsal, not hope that two uploads stayed aligned.
  2. D1 locks one build_id for the candidate rehearsal night.
  3. D2 documents itch and Steam in channels[] with URLs and upload UTC.
  4. D3 requires channel_label_match across visible labels—or a numbered exception.
  5. D4 proves bytes with etag / SHA-256 / steam_build_id, not upload progress bars.
  6. D5 smokes itch browser and Steam client, not Editor-only paths.
  7. D6 files dual_sku_dress_rehearsal_receipt_v1.json and sets dual_sku_rehearsal_ok.
  8. Append two upload_log.csv rows—one per portal, same build_id.
  9. Thursday row review consumes channel_label_match; this tutorial prevents R6 surprises.
  10. Economics worksheet answers runway; this tutorial answers which build players get.
  11. Re-run rehearsal when either channel uploads—even if the other did not change.
  12. Forward blog #5 when rehearsal was green but wishlists still split after launch.

FAQ

What is a dual-depot dress rehearsal?

A one-evening evidence pass that proves itch and Steam fest channels reference the same build_id intent, agree on channel_label_match, and boot on both surfaces before October upload peak.

Is this the same as Thursday BUILD_RECEIPT row review?

No. Thursday diffs receipt rows weekly (R1–R10). This tutorial creates the initial dual-channel proof that makes R6 trustworthy.

Do itch and Steam binaries need identical SHA-256 hashes?

Not when surfaces differ (HTML5 vs Win64). D4 proves each channel serves its correct candidate for the declared build_id.

Can I skip itch if we only care about Steam fest?

Yes if itch is not a fest-facing channel. Remove itch from marketing and D2—or include it in the rehearsal.

How does this relate to localization freeze?

If both SKUs claim the same locales, run freeze evening first and link its receipt path in D6.

What if channel_label_match is false on purpose?

Document label_exception_id, owner, and expiry. Keep dual_sku_rehearsal_ok false until labels align or exception is approved for promotion.

Does this replace itch Butler QA tutorials?

No. Use itch ninety-minute QA pass for upload mechanics; this article owns cross-channel receipt proof.

When should we re-run the rehearsal?

After any upload to itch or Steam that changes player-visible bytes or labels—even mid-September.

Related reads

Closing

October upload peak is the wrong week to learn itch and Steam were never the same build. Spend one evening on the channel matrix, label match, byte proof, and dual smoke—then file dual_sku_rehearsal_ok only when D1–D6 are honestly green. If wishlists still stall after GREEN bytes, run the wishlist-split recovery case study (R1–R6) instead of declaring fest discovery healthy.

When marketing asks to push the itch link before Steam is ready, you will have a receipt that says yes or no with evidence—not vibes. That is the whole job of a dual-depot dress rehearsal before October Next Fest.