BUILD_RECEIPT channel_label_match False When itch HTML5 Added as Third Channel — How to Fix
Problem: CI is green for Steam + GX.games—gx_html5_upload_receipt_v1.json and build_label_smoke_receipt_v1.json both show matching labels. You add itch.io HTML5 as a third public surface and channel_label_match flips to false on BUILD_RECEIPT even though each channel “looks fine” in isolation.
Who is affected now: July 2026 HTML5-first teams running itch + GX + Steam per the HTML5 second storefront analysis. You fixed GX receipt vs Steam build_label and itch CDN WASM MIME—this article is triple-channel normalization, not MIME-only or pairwise Steam/GX drift alone.
Fastest safe fix: One repo-root VERSION string → map itch build_label from that file (not raw itch game ID) → add itch_html5 row to playtest_scope_map_v1.json → upload all three from same git_sha → file triple_channel_label_receipt_v1.json with normalized compare → set BUILD_RECEIPT channel_label_match: true only when Steam, GX, and itch rows pass → include itch column in Wednesday smoke.
Direct answer
channel_label_match is a cross-channel boolean—it fails when any surface’s logged label differs from VERSION after normalization (trim, case fold for compare only, never change player-visible casing). itch often logs 1234567 (numeric game id) or MyGame slug while Steam shows fest-demo-2026-05-24-rc2—pairwise GX+Steam scripts never compared itch, so the third row exposes the gap.
Why this issue spikes in July 2026
- Triple HTML5 surfaces — itch for jam/fest links, GX for discovery, Steam for wishlist—three upload rituals, one tired producer.
- Pairwise CI — Pipelines written when only Steam+GX existed; itch job added without extending diff gate.
- itch field names — Dashboard “Version” ≠ BUILD_RECEIPT
build_label; facilitators paste game URL slug. - Case sensitivity —
Fest-Demo-RC2vsfest-demo-2026-05-24-rc2fails strict string compare. - Missing scope map row —
playtest_scope_maplistsgx_public+fest_publicbut noitch_public→ receipt aggregator marks mismatch.
Symptoms and search phrases
- BUILD_RECEIPT:
channel_label_match: falseafter itch upload job added. - Steam overlay + GX overlay match; itch page shows different build string or none.
itch_html5_upload_receiptmissing fromrelease-evidence/itch/.- CI: Steam job + GX job green; itch job green but no label diff step.
- itch URL uses
?v=2cache bust; Steam stillrc1. - Facilitator README lists GX + Steam only.
wasm_mime_receiptpass but channel row still false.
Root causes (check in order)
- itch receipt logs game ID, not
build_label— numeric id ≠ VERSION string. - No
itch_publicsurface in scope map — aggregator defaults fail. - Case / whitespace drift —
Fest-Demovsfest-demo. - itch uploaded from different commit — MIME pass, label stale.
- Separate
ITCH_VERSIONdefine — HTML5 export preset not reading rootVERSION. - GX+Steam normalized compare; itch raw string — inconsistent CI function.
- itch custom domain — live URL differs from
*.itch.iojob artifact path (label still must match).
Beginner path (first 50 minutes)
Prerequisites: Repo VERSION file, three live URLs (itch tab, GX, installed Steam), last three upload receipts from CI.
- Read
VERSION→ write expected stringE. - Open itch game in private window → note HUD
build_label(add overlay if missing). - Open GX live URL → note overlay.
- Install Steam fest build → note overlay.
- If any ≠
Eafter trim → fix before re-sharing itch link. - Open BUILD_RECEIPT JSON → find
channel_label_matchand per-channel rows.
Common mistake: Treating itch WASM MIME pass as proof all HTML5 channels align.
Fastest safe fix path
Step 1 — Single VERSION source (all three channels)
fest-demo-2026-05-24-rc2
| Surface | Must read VERSION |
|---|---|
| Steam NW.js / desktop | Title-screen Label / version.txt |
| GX.games HTML5 | Export inject + upload metadata |
| itch.io HTML5 | Same artifact folder as GX when possible; itch “Version” field = VERSION |
Wire Godot/Construct/GameMaker per GX receipt help—same injection into itch zip.
Step 2 — itch_html5_upload_receipt_v1.json
After itch upload (butler / web UI / CI):
{
"schema": "itch_html5_upload_receipt_v1",
"uploaded_at_utc": "2026-05-24T23:40:00Z",
"git_sha": "a1b2c3d4",
"itch_game_id": "1234567",
"itch_slug": "your-game-slug",
"build_label": "fest-demo-2026-05-24-rc2",
"live_url": "https://your-team.itch.io/your-game",
"post_upload_mime_ok": true,
"wasm_mime_receipt_ref": "wasm_mime_receipt_v1.json",
"pass": true
}
Critical: build_label must equal VERSION—log itch_game_id separately; never compare game id to VERSION in CI.
Store: release-evidence/itch/itch_html5_upload_receipt_v1.json.
Step 3 — Extend GX + Steam receipts (pairwise baseline)
Ensure existing receipts from same pipeline run:
release-evidence/gx/gx_html5_upload_receipt_v1.json—build_id=VERSIONrelease-evidence/steam/build_label_smoke_receipt_v1.json—steam_festchannel pass
See 14 Free GX multi-channel HTML5 tools.
Step 4 — triple_channel_label_receipt_v1.json
{
"schema": "triple_channel_label_receipt_v1",
"checked_at_utc": "2026-05-24T23:45:00Z",
"version_file_sha256": "sha256:...",
"expected_build_label": "fest-demo-2026-05-24-rc2",
"normalize": { "trim": true, "compare_case_fold": true },
"channels": [
{
"channel": "steam_fest",
"surface": "fest_public",
"logged_label": "fest-demo-2026-05-24-rc2",
"normalized_match": true,
"pass": true
},
{
"channel": "gx_html5",
"surface": "gx_public",
"logged_label": "fest-demo-2026-05-24-rc2",
"normalized_match": true,
"pass": true
},
{
"channel": "itch_html5",
"surface": "itch_public",
"logged_label": "fest-demo-2026-05-24-rc2",
"normalized_match": true,
"pass": true
}
],
"channel_label_match": true,
"git_sha": "a1b2c3d4",
"pass": true
}
Rule: channel_label_match on this receipt must equal BUILD_RECEIPT column—single aggregator script writes both.
Step 5 — Normalize function (CI)
def norm_label(s: str) -> str:
return s.strip().casefold()
def channel_label_match(version: str, *labels: str) -> bool:
v = norm_label(version)
return all(norm_label(x) == v for x in labels)
# Usage after loading three receipts
ok = channel_label_match(
open("VERSION").read(),
gx_receipt["build_id"],
steam_receipt["channels"][0]["installed_build_label"],
itch_receipt["build_label"],
)
if not ok:
raise SystemExit("channel_label_match false")
| Pitfall | Fix |
|---|---|
Compare itch_game_id to VERSION |
Use build_label field only |
| Case-sensitive compare | Use casefold() for gate; display original casing in HUD |
| itch job on old artifact | Pin same git_sha artifact path for all HTML5 uploads |
Step 6 — playtest_scope_map_v1.json surface tags
{
"surfaces": [
{
"surface_id": "fest_public",
"channel": "steam",
"build_label": "fest-demo-2026-05-24-rc2",
"demo_scope": "fest_hour_one"
},
{
"surface_id": "gx_public",
"channel": "gx_html5",
"build_label": "fest-demo-2026-05-24-rc2",
"demo_scope": "fest_hour_one"
},
{
"surface_id": "itch_public",
"channel": "itch_html5",
"build_label": "fest-demo-2026-05-24-rc2",
"demo_scope": "fest_hour_one"
}
]
}
Missing itch_public is the #1 scope-map cause of false channel_label_match when itch is live but unmapped.
Step 7 — BUILD_RECEIPT columns
| Column | Example |
|---|---|
build_label |
fest-demo-2026-05-24-rc2 |
git_sha |
a1b2c3d4 |
steam_depot_upload_ok |
true |
gx_upload_ok |
true |
itch_upload_ok |
true |
wasm_mime_receipt |
pass |
channel_label_match |
true |
triple_channel_label_receipt |
pass |
Attach paths: upload_log[] entries for steam, gx, itch receipts.
Block fest / public itch link when channel_label_match: false.
Working dev path — proof table
| Check | Steam | GX | itch |
|---|---|---|---|
Overlay shows VERSION |
Photo | Private-window photo | Private-window photo |
Receipt build_label / build_id |
smoke receipt | gx receipt | itch receipt |
Same git_sha |
Yes | Yes | Yes |
post_upload_mime_ok |
N/A | optional | required for Godot |
| Scope map row | fest_public |
gx_public |
itch_public |
Run 12 Free itch HTML5 WASM MIME cross-check tools before declaring HTML5 stack green.
Verification checklist
- [ ]
VERSIONmatches all three overlays (photos in CI artifacts) - [ ]
triple_channel_label_receipt_v1.jsonchannel_label_match: true - [ ] BUILD_RECEIPT
channel_label_match: true - [ ]
itch_publicpresent in scope map with samebuild_label - [ ] itch receipt uses
build_label, not game id, for compare - [ ] All uploads from same
git_sha - [ ] Wednesday smoke spreadsheet has itch column
- [ ] WASM MIME receipt pass for itch (Godot/Construct)
Prevention
- One pipeline, three upload jobs—same artifact directory.
- Never add itch to README without extending CI diff script.
- Template
triple_channel_label_receipt_v1.jsonin reposchemas/. - Ban manual itch “Version” edits outside
VERSIONfile generation. - Pair facilitators with GX multi-channel resource checklist.
Troubleshooting
| Symptom | Fix |
|---|---|
| Steam+GX pass, itch fail | itch build_label field + scope map row |
| itch pass after lowercase-only fix | Document compare uses casefold; HUD keeps display case |
| All receipts pass, BUILD_RECEIPT false | Aggregator reads stale itch path or wrong JSON key |
| itch numeric id in receipt | Add build_label; stop comparing itch_game_id |
| MIME fail, label match | Fix itch CDN MIME first |
| Custom domain itch | Label gate unchanged; CORS may need Construct CORS help |
FAQ
Can channel_label_match be true if demo scopes differ per channel?
Labels must match when scopes match. If itch is a jam slice and Steam is fest hour-one, use different intentional build_label values and set channel_label_match: false with documented reason—do not silently drift.
Do I need three separate HTML5 builds?
Prefer one export artifact uploaded to itch and GX; Steam may be NW.js wrapper around same VERSION—still log three channel receipts.
GX+Steam was green before itch—what changed?
The aggregator now includes a third label; pairwise scripts were incomplete.
Is this the same as Steam depot promotion mismatch?
No—Steam depot build_label is wrong binary on Steam. This article is cross-surface label on HTML5 triple upload.
Related links
- GX.games Receipt build_id vs Steam build_label
- itch.io CDN WASM MIME After Upload
- Steam Depot Promoted Playtest build_label Mismatch
- 14 Free GX.games Multi-Channel HTML5 build_label Tools
- 12 Free itch.io HTML5 Upload Receipt WASM MIME Cross-Check Tools
- HTML5-First Studios Adding a Second Storefront (2026)
- Your First BUILD_RECEIPT JSON and Upload Log
- Wednesday Demo Build Smoke Ritual
- Official: itch.io — Uploading HTML5 games, GX.games documentation, Steamworks — Builds
Add the itch column when you add the itch URL—pairwise green on Steam+GX is not triple-channel proof.