Stop Shipping Content Updates Without a Numbered PATCH Exception List - 2026 Opinion

April 8: someone says "it's just a remote catalog bump—players will get it overnight." April 9: warm catalog evening was GREEN three builds ago. April 11: weekly catalog jq shows a silent hash change with no row on any exception list. April 14: Sale weekend hits; orphan recovery folklore returns because a "quick push" left dead bundle URLs warm in CDN cache.
April–June 2027 is when compassionate content updates destroy the Addressables evidence trail you built after Friday March capstone. SHIP exception opinion disciplined holiday freeze → re-entry. HOTFIX exception opinion disciplined live-week D1–D7 binary promotion. MERGE exception opinion disciplined post-fest trunk integration. Content-update season needs a cousin list with different stakes: every ship that changes remote catalogs, content catalogs, or player-visible Addressables labels during April–June patch weeks must appear as the next integer in patch_exception_list_receipt_v1.json—not "quick push," not "we'll document after Sale," not verbal producer approval in #content-update.
This Opinion & Hot Takes piece makes a narrow claim: during April–June 2027 content-update / Addressables weeks, no catalog or content hash that players can fetch ships unless the change is PATCH-00N with same-week warm catalog (or Wednesday smoke cousin), catalog_hash_after lineage on BUILD_RECEIPT, and producer sign-off—refuse pushes that skip the next number even when Summer Sale panic is loud.
Non-repetition note: SHIP owns holiday O1–O6. HOTFIX owns live-week H1–H6. MERGE owns post-fest M1–M6 trunk merges. Warm catalog tutorial owns A1–A6 evening path. jq April weekly owns J1–J6 catalog hash diffs. Orphan recovery owns R1–R6 after ghosts. This URL owns numbered PATCH exception list + P1–P6 gates when content-update ships collide with Sale adjaceny.
Pair MERGE cousin, HOTFIX, SHIP, orphan recovery #5, jq April weekly #8, forward blog #11 Thursday content-update row review.
Why this matters now (April 2027 Addressables season)
- "Just push the catalog" culture — Teams treat remote catalogs as disposable after warm GREEN; silent hash bumps hide until Sale traffic.
- jq weekly silent hash changes — April catalog jq J5 RED routes to PATCH opinion—this URL supplies PATCH-00N format.
- Orphan recurrence — Orphan recovery proves warm GREEN ≠ purge honesty; unnumbered pushes recreate ghosts.
- Summer Sale adjacency — Sale store challenge and wishlist decay assume catalog truth; PATCH theater wastes sale week on CDN archaeology.
- Thursday promotion — Forward #11 expects
patch_exception_counton BUILD_RECEIPT beforecontent_update_promote_allowed.
Direct answer: Maintain patch-exception-list-2027-content-update.md with monotonic PATCH-001…N, file patch_exception_list_receipt_v1.json, set BUILD_RECEIPT patch_exception_list_ok: true, run warm catalog / smoke same ISO week any exception touches player-fetchable catalogs—refuse CDN publishes that skip the next number.
The opinion in one paragraph
Content-update season is not "ops cleanup" where catalog hygiene is optional. If you bump a remote catalog without incrementing a numbered PATCH exception, you did not ship a patch—you created a second catalog timeline: last week's warm receipt, this week's silent hash, Sale weekend players fetching orphans, and partners reading orphan recovery folklore again. Micro-studios cannot afford two catalog stories before Summer Sale. Numbered PATCH lists are the minimum correlation layer between catalog hashes, CDN labels, BUILD_RECEIPT rows, and the warm ritual you already claim to respect. Verbal "we pushed Addressables" is how teams enter Sale week on stale warm GREEN while stand-up claims catalog is current.
Who this opinion is for
| Audience | Why read |
|---|---|
| Solo founder mid-patch | Copy-paste "next PATCH number or no CDN publish" |
| Producer on April content-update | Block push until list matches warm + catalog hash |
| Engineer / tech art | Wire verify_patch_exception_list.sh before promote |
| Live-ops lead facing Sale | Refuse silent hash bumps before discount weekend |
SHIP vs HOTFIX vs MERGE vs PATCH — format ladder
| List | Window | Schema | Primary gate |
|---|---|---|---|
| SHIP exception | Dec freeze → Jan re-entry | ship_exception_list_receipt_v1 |
O1–O6 |
| HOTFIX exception | Feb live D1–D7 | hotfix_exception_list_receipt_v1 |
H1–H6 |
| MERGE exception | Mar post-fest W10–W13 | merge_exception_list_receipt_v1 |
M1–M6 |
| PATCH exception | Apr–Jun content-update | patch_exception_list_receipt_v1 |
P1–P6 |
| jq April weekly | Weekly ISO | april_content_update_receipt_v1 |
J1–J6 |
Rule: MERGE / HOTFIX / SHIP rows do not substitute for PATCH rows when the ship changes remote catalog hashes—archive MERGE list at March capstone; start PATCH list at PATCH-001 on first April content-update that touches player-fetchable Addressables.
Beginner path — numbered list in 25 minutes
Copy into release-evidence/content-update/patch-exception-list-2027-april.md:
# Numbered PATCH exception list (April–June 2027 content-update)
patch_window: 2027-04-content-update
list_owner: producer
next_seq: 2
scope_policy: remote_catalog | content_catalog | addressables_label only
| Seq | ISO week | Summary | catalog_hash_before | catalog_hash_after | Touches CDN? | Warm same week? | Owner |
|-----|----------|---------|---------------------|--------------------|--------------|-----------------|-------|
| PATCH-001 | 2027-W15 | Event skin remote catalog bump | a1b2… | c3d4… | yes | yes | tech-art |
| PATCH-002 | (reserved) | — | — | — | — | — | — |
Beginner rules:
- Next content-update ship must be
next_seq—no PATCH-003 before PATCH-002 exists. - CDN / public catalog touch ⇒ warm (or smoke) same ISO week—cousin warm catalog evening.
catalog_hash_aftermust update on BUILD_RECEIPT / weekly jq when the ship is player-fetchable.- Store-capsule-only edits ⇒ not PATCH—use art / store receipts; PATCH list is catalog identity, not screenshot polish.
Success check: Producer can answer "how many content-update ships hit CDN?" by reading highest Seq integer.
Developer path — gates P1–P6
| Gate | Check | Fail action |
|---|---|---|
| P1 | exceptions[] seq integers 1..N contiguous |
Fix gaps before promote |
| P2 | next_seq == N+1 in markdown matches JSON |
Sync list + receipt |
| P3 | touches_cdn: true ⇒ warm_catalog_receipt_path (or smoke) same ISO week |
Block CDN publish |
| P4 | Last catalog_hash_after matches BUILD_RECEIPT / weekly catalog_hash |
Fix hash lineage |
| P5 | scope matches policy (remote_catalog / content_catalog / addressables_label) |
Reject out-of-scope ships here |
| P6 | patch_exception_list_ok: true + producer sign-off path |
Fail jq weekly / promote bundle |
patch_exception_list_receipt_v1.json (template)
{
"schema": "patch_exception_list_receipt_v1",
"patch_window": "2027-04-content-update",
"generated_at": "2027-04-08T18:00:00Z",
"next_seq": 2,
"scope_policy": "remote_catalog|content_catalog|addressables_label",
"exceptions": [
{
"seq": 1,
"id": "PATCH-001",
"iso_week": "2027-W15",
"summary": "Event skin remote catalog bump",
"scope": "remote_catalog",
"touches_cdn": true,
"touches_player_fetch": true,
"catalog_hash_before": "a1b2c3d4e5f6",
"catalog_hash_after": "c3d4e5f6a1b2",
"warm_required": true,
"warm_catalog_receipt_path": "release-evidence/content-update/PATCH_001_WARM.json",
"cdn_purge_noted": true,
"producer_signoff_path": "release-evidence/content-update/PATCH_001_SIGNOFF.json",
"owner": "tech-art",
"april_weekly_receipt": "release-evidence/content-update/APRIL_CONTENT_UPDATE_RECEIPT_W15.json",
"orphan_risk_note": "purge candidates listed in warm notes"
}
],
"pointers": {
"warm_catalog_evening": "release-evidence/content-update/APRIL_WARM_CATALOG_RECEIPT.json",
"april_weekly_jq": "release-evidence/content-update/APRIL_CONTENT_UPDATE_RECEIPT.json",
"orphan_recovery": "release-evidence/content-update/REMOTE_CATALOG_ORPHAN_RECOVERY_RECEIPT.json",
"march_capstone": "release-evidence/capstone/MARCH_CAPSTONE_CLOSEOUT_RECEIPT.json"
},
"gates": {
"P1": "pass",
"P2": "pass",
"P3": "pass",
"P4": "pass",
"P5": "pass",
"P6": "pass"
},
"patch_exception_list_ok": true
}
Store canonical copy at release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json.
BUILD_RECEIPT root rows
{
"patch_exception_list_ok": true,
"patch_exception_count": 1,
"patch_exception_list_receipt": "release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json",
"catalog_hash": "c3d4e5f6a1b2",
"warm_catalog_ok": true,
"april_content_update_weekly_ok": true
}
Fail-closed: If patch_exception_count ≠ exceptions[].length or last catalog_hash_after ≠ catalog_hash, promote gate fails.
verify_patch_exception_list.sh
#!/usr/bin/env bash
set -euo pipefail
LIST="${1:-release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json}"
BR="${2:-BUILD_RECEIPT.json}"
jq -e '.schema == "patch_exception_list_receipt_v1"' "$LIST" || exit 1
COUNT=$(jq '.exceptions | length' "$LIST")
for i in $(seq 1 "$COUNT"); do
ACT=$(jq -r --argjson n "$i" '.exceptions[] | select(.seq == $n) | .seq' "$LIST")
[[ "$ACT" == "$i" ]] || { echo "P1 fail: missing seq $i"; exit 2; }
done
jq -e '
[.exceptions[] | select(.touches_cdn == true)
| .warm_catalog_receipt_path] | all(. != null and . != "")
' "$LIST" || { echo "P3 fail: CDN touch without warm path"; exit 3; }
LAST=$(jq -r '.exceptions[-1].catalog_hash_after' "$LIST")
CH=$(jq -r '.catalog_hash // .content_catalog_hash' "$BR")
[[ "$LAST" == "$CH" ]] || { echo "P4 fail: catalog hash mismatch"; exit 4; }
jq -e '.patch_exception_list_ok == true' "$LIST" || exit 6
echo "patch_exception_list verify: OK"
Exit codes: 1 schema, 2 P1 gaps, 3 P3 warm, 4 P4 hash, 5 P5 scope, 6 P6 latch.
Patch theater vs honest PATCH exception
| Patch theater | Honest PATCH exception |
|---|---|
| "Hotfix the catalog Friday" | PATCH-00N filed before CDN publish |
| "Warm was GREEN last week" | Same-week warm receipt path |
| "Sale will flush cache" | cdn_purge_noted + orphan risk note |
| "Hash changed—fine" | catalog_hash_after on BUILD_RECEIPT |
| Verbal producer OK | producer_signoff_path on disk |
jq weekly integration (J5 cousin)
When April catalog jq row_diff shows catalog hash bump:
jq -r '.exceptions[-1].id' release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json
Must equal latest PATCH id referenced in weekly receipt notes—or weekly diff is J5 RED until PATCH row filed.
Worked example — synthesized PATCH-001
| Step | Action | Result |
|---|---|---|
| W15 Mon | Capstone latch complete; warm path known | Ready for first April ship |
| W15 Tue | Tech-art proposes remote catalog bump | Producer requests PATCH-001 |
| W15 Tue | File exception row + warm | P3 GREEN |
| W15 Wed | Publish catalog; update BUILD_RECEIPT hash | P4 GREEN |
| W15 Wed | jq weekly verify | Weekly ok + patch count aligned |
| W15 Thu | Forward row-review promotes | PATCH list proves Sale can inherit truth |
Producer rule: No content-update contractor week billed without PATCH id on invoice notes field.
Proof table
| Evidence | Proves | Fail signal |
|---|---|---|
| PATCH-001…N markdown | P2 monotonic seq | Gap at PATCH-003 |
| warm_catalog_receipt_path | P3 CDN discipline | Push without warm |
| BUILD_RECEIPT catalog_hash | P4 lineage | Hash stale vs CDN |
| cdn_purge_noted | Orphan hygiene | Ghosts after Sale |
| verify exit 0 | P6 latch | Verbal "pushed" |
| jq weekly crosswalk | J5 wiring | Silent hash without PATCH id |
Scenarios A–G
| ID | Situation | Move |
|---|---|---|
| A | Standard remote catalog bump | Full P1–P6 before CDN |
| B | Zero content updates this window | patch_exception_count: 0; patch_exception_list_ok: true with empty exceptions[] |
| C | Local-only Addressables editor test | Not PATCH—no player-fetch, no list row |
| D | Solo tech-art, one bump | Same rules—seq still monotonic |
| E | CDN touch without purge note | P3 fail until purge / orphan note filed |
| F | jq weekly J5 RED | Pause pushes until PATCH row exists |
| G | Sale weekend starting Friday | PATCH list GREEN before discount go-live |
Engine / CDN notes
| Tool | PATCH discipline |
|---|---|
| Unity Addressables | Catalog hash in exception; pair drift loop |
| Godot / custom remote packs | Treat pack manifest hash as catalog_hash_* |
| CloudFront / Fastly | PATCH row notes purge ID when touches_cdn |
| Steam depot content | Depot bumps that change fetchable packs still need PATCH if players get new catalogs |
Printable producer card
# Content-update PATCH gate (before CDN / catalog publish)
[ ] Next seq = PATCH-00N in markdown + JSON
[ ] Warm catalog (or smoke) same ISO week if CDN / player-fetch
[ ] BUILD_RECEIPT catalog_hash matches catalog_hash_after
[ ] verify_patch_exception_list.sh exit 0
[ ] April weekly jq GREEN (or freeze week documented)
[ ] Orphan risk / CDN purge noted when applicable
Signed: __________
Counterarguments (honest limits)
"Warm evening already proves the catalog."
Warm proves one label path at one moment—orphan recovery shows ghosts survive. PATCH adds numbered ship identity across weeks.
"This is the same as MERGE list."
Different window, schema, and gates—cousin discipline, not copy-paste JSON. MERGE = trunk branch integration; PATCH = catalog/CDN identity.
"Unity doesn't require PATCH lists."
Correct—this is team ops before Sale and partner diligence.
"Empty weeks waste paperwork."
File empty exceptions[] with latch true—proves intentional freeze, not forgotten process.
"Hotfix means no paperwork."
HOTFIX list was for fest live binaries. Addressables mid-patch is PATCH—do not reuse HOTFIX-00N numbers across years without a new schema.
Format ladder reminder (do not cannibalize)
| Intent | URL role |
|---|---|
| Strategy / stop-shipping opinion | This PATCH URL |
| Evening warm tutorial | Warm catalog A1–A6 |
| Weekly hash detection | jq April J1–J6 |
| Ghost recovery case study | Orphan R1–R6 |
| Store Sale response | Summer Sale S1–S5 challenge |
Same cluster; different primary keyword and artifact.
Monday stand-up script (90 seconds)
Producer: Last PATCH id? ____ next_seq? ____
Tech-art: Any CDN publish this week? Y/N
If Y: warm path filed? Y/N catalog_hash on BUILD_RECEIPT? Y/N
jq weekly: GREEN / J5 RED / freeze week
Sale within 14 days? If yes, no silent bumps—PATCH or defer.
Tape beside the warm catalog checklist so stand-up cannot invent a fourth ship without a number.
Comparison pack — exception lists at a glance
| Question | SHIP | HOTFIX | MERGE | PATCH |
|---|---|---|---|---|
| What changes? | Holiday unfreeze ship | Live fest binary | Post-fest trunk merge | Catalog / CDN content update |
| Monotonic id | SHIP-00N | HOTFIX-00N | MERGE-00N | PATCH-00N |
| Same-week proof | Smoke | Smoke | Smoke | Warm (or smoke) |
| Lineage field | build_label_after | build_label_after | build_label_after | catalog_hash_after |
| Typical season | Dec–Jan | Feb live | March sprint | Apr–Jun |
Reusing HOTFIX ids for April Addressables is how diligence zips confuse reviewers.
Relationship to orphan recovery and weekly jq
| Artifact | Role |
|---|---|
| Warm catalog A1–A6 | Candidate path GREEN |
| PATCH-00N | Authorized ship identity for that bump |
| jq April weekly J1–J6 | Detect silent hash without PATCH |
| Orphan R1–R6 | Recover when authorization/path honesty failed |
Order: authorize PATCH → warm → publish → weekly diff → orphan only if ghosts remain.
Waiver notes (when RED is intentional)
{
"waiver_id": "PATCH-WAIVER-2027-04-12",
"gates_overridden": ["P3"],
"reason": "CDN already purged; warm deferred 18 hours with pager on-call",
"catalog_hash_after": "c3d4e5f6a1b2",
"signed_by": "producer",
"expires_iso_week": "2027-W15"
}
Infinite waivers defeat P6—review waivers in the Monday stand-up beside forward blog #11 (Thursday content-update row review) once published.
PowerShell verify cousin (Windows)
$rec = Get-Content release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json -Raw | ConvertFrom-Json
if ($rec.schema -ne "patch_exception_list_receipt_v1") { throw "schema" }
$seqs = $rec.exceptions.seq | Sort-Object
for ($i = 1; $i -le $seqs.Count; $i++) {
if ($seqs[$i-1] -ne $i) { throw "P1 gap at $i" }
}
if (-not $rec.patch_exception_list_ok) { throw "latch" }
"OK patch_exception_list"
CI sketch (optional)
name: april-patch-exception-list
on:
pull_request:
paths:
- "release-evidence/content-update/PATCH_EXCEPTION_LIST_RECEIPT.json"
- "BUILD_RECEIPT.json"
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: bash scripts/verify_patch_exception_list.sh
Worked narrative — Sale-weekend near miss
Studio East filed warm GREEN on April 3, then pushed three silent catalog hashes through April 10 without PATCH rows. Weekly jq stayed RED on J5; nobody read it. Sale opened April 12; Discord filled with MissingReferenceException ghosts. They ran orphan recovery mid-sale and lost two days of wishlist velocity work.
Studio West started PATCH-001 on the first April bump, refused a Friday "just one more skin pack" without P3 warm, and entered Sale with patch_exception_list_ok: true and matching weekly hashes. Quieter store week; faster partner zip.
Common mistakes
Reusing HOTFIX-00N for Addressables — Wrong schema and window.
Filing PATCH after CDN publish — Theater; seq must pre-authorize.
Warm last week, ship this week — P3 fails without same-week warm.
Capsule art only labeled as PATCH — Out of scope; don't pollute catalog list.
Zero exceptions without receipt — Still file latch-true empty list.
Ignoring J5 RED — Weekly silence becomes Sale noise.
Outbound references
- Unity Addressables documentation — catalog / content update mechanics
- Steamworks branches — when depot + catalog ships combine
- Steamworks documentation — store week ops context
Related GamineAI reads
- MERGE exception opinion
- HOTFIX exception opinion
- SHIP exception opinion
- jq April content-update weekly catalog row diff
- Remote catalog orphan recovery case study
- April warm catalog evening
- Addressables remote catalog drift loop
- Friday March capstone before April content update
- Thursday Content Update BUILD_RECEIPT Row Review After Warm Catalog —
content_update_promote_allowedafter PATCH + warm
Key takeaways
- April–June content-update ships need PATCH-001…N—not verbal "quick catalog push."
patch_exception_list_receipt_v1.jsongates P1–P6 with warm path + catalog hash lineage.patch_exception_list_okon BUILD_RECEIPT latches patch discipline.- SHIP + HOTFIX + MERGE cousins—different windows.
- jq April weekly J5 routes silent hash bumps here.
- Orphan recovery is the failure mode when PATCH theater wins.
cdn_purge_notedwhen CDN touch—Sale week depends on honesty.- Thursday content-update row review expects stable
patch_exception_countbeforecontent_update_promote_allowed. - Empty
exceptions[]allowed when zero ships—still file receipt. - Warm GREEN last week does not authorize this week's CDN publish.
- Do not reuse HOTFIX numbers for Addressables mid-patch.
verify_patch_exception_list.shfail-closes hash mismatch.- Invoice notes should carry PATCH id for contractor weeks.
- Capsule-only art is out of PATCH scope—keep the list catalog-identity clean.
FAQ
Is this the same as the MERGE exception list?
No—MERGE owns post-fest trunk branch merges; PATCH owns catalog/CDN content-update ships.
Is this the same as HOTFIX?
No—HOTFIX owns fest live-week binary promotion; Addressables mid-season is PATCH.
Do we need PATCH if warm catalog already passed?
Yes—warm proves a path; PATCH numbers the authorized ship across weeks.
What if we ship zero content updates in April?
File latch-true receipt with empty exceptions[].
How does orphan recovery relate?
Orphan is the recovery playbook after ghosts; PATCH is prevention via numbered ships.
When does Thursday row review (#11) use this?
It should refuse content_update_promote_allowed while patch_exception_list_ok is false after a hash bump.
Can Godot teams use this?
Yes—map pack manifest hashes to catalog_hash_* fields.
Does Unity require this JSON?
No—team ops / diligence only.
Closing
Content updates without a numbered PATCH list are theater—authorize PATCH-00N, warm the same week, latch patch_exception_list_ok, and let weekly catalog jq prove the hash matches. Pair MERGE / HOTFIX / SHIP cousins for the right window, and keep orphan recovery as the Sale-week ambulance—not the default process. Silent catalog pushes are how micro-studios buy Discount Week with CDN archaeology.