From 0d8d59a22a818b860742a0dfc2b7364e2b5669cc Mon Sep 17 00:00:00 2001 From: m3ultra Date: Fri, 17 Jul 2026 02:18:37 +1000 Subject: [PATCH] =?UTF-8?q?Lane=20C=20R25:=20sku-derived=20slot=20ids=20?= =?UTF-8?q?=E2=80=94=20F's=20id-collision=20finding=20made=20structural=20?= =?UTF-8?q?(ledger=20#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One contract line, published first so G re-emits against it. §7.2a added. THE COLLISION, MEASURED: both packs number items positionally from zero, so all 120 godverse ids are also generic-pack ids — 120/120 overlap. An id-equality gate cannot discriminate which pack a rendered item came from; that is exactly why the brief's own #7a spec was vacuous. (The vacuous-gate law's first application broke the law — and my own R23 metric had the same disease.) THE RULE (§7.2a): items[].id MUST derive from the source's stable key, never from position, and MUST be unique across ALL packs. Real-stock packs: id = "sku_" + (G's snapshot already carries `sku` per item, e.g. 20260314070442 — it's just dropped at emit, so this is index-only). Generic packs keep rec_NNNN as their namespace; real-stock must not emit into it. TWO REASONS, and the second outlives the gate: 1. positional ids collide by construction (the 120/120 above) -> id-equality gates can't discriminate. Sku ids fix that permanently, now, at two atlases instead of fifty. 2. POSITION IS NOT IDENTITY: a positional id renumbers on every re-bake — sell one record out of crate 550 and rec_0050 silently becomes a different record. Anything holding an id across snapshots (a wallet purchase, a saved dig, a gate assertion, and above all tier-2's sold-means-gone) would point at the wrong item. The POS sku is stable because it is the shop's own identity for that copy (release_id identifies the pressing, not the copy). So this is also the identity tier-2 will need, bought now for free. SCOPE: index-only — no atlas bytes, no town cache, no goldens, no C code (id isn't read by the loader, §7.3; the dig/buy consumers and gates read it). -> G: re-emit with sku_ ids. -> E: assert the form (present, unique within pack, sku_-prefixed). Committed by explicit pathspec — F's six staged files sit untouched, as its session left them. Co-Authored-By: Claude Opus 4.8 --- C-progress.md | 34 +++++++++++++++++++++++++++++++++- docs/LANES/LANE_C_PUB.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/C-progress.md b/C-progress.md index 2227950..45ff1a8 100644 --- a/C-progress.md +++ b/C-progress.md @@ -3,7 +3,39 @@ *Status: **v1 complete & verified**. Standalone interiors library + test page. Every shop door opens into a unique, seeded, themed interior, generated on demand in ~4ms, byte-identical every revisit.* -Last updated: 2026-07-17 (round 24) · owner: PROCITY-C · reviewer: Fable +Last updated: 2026-07-17 (round 25) · owner: PROCITY-C · reviewer: Fable + +--- + +## Update 2026-07-17 (round 25, the tag earned) — sku-derived slot ids (ledger #3, C's one line) + +R25 §Lane C: one contract line, published first so G re-emits against it. **§7.2a added** — F's id-collision +finding made structural, while there are two atlases to migrate instead of fifty. + +**The collision, measured:** both packs number items positionally from zero, so **all 120 godverse ids are +also generic-pack ids — 120/120 overlap.** An id-equality gate cannot discriminate which pack a rendered +item came from; that's exactly why the brief's own #7a spec was vacuous. (The vacuous-gate law's first +application broke the law — and my own R23 metric had the same disease. It's a persistent species.) + +**The rule (§7.2a):** `items[].id` MUST derive from the source's stable key, never from position, and MUST +be unique across ALL packs. Real-stock packs: **`id = "sku_" + `** — G's snapshot already carries +`sku` per item (e.g. `20260314070442`), it's just dropped at emit, so this is index-only. Generic packs keep +`rec_NNNN` as their namespace; real-stock must not emit into it. + +**I gave it two reasons, and the second is the one that outlives the gate.** The collision is the presenting +symptom. The deeper defect is that **position is not identity**: a positional id renumbers on every re-bake, +so selling one record out of crate 550 silently makes `rec_0050` a *different* record. Anything holding an +id across snapshots — a wallet purchase, a saved dig, a gate assertion, and above all **tier-2's +sold-means-gone** — would point at the wrong item. The POS sku is stable because it's the shop's own +identity for that copy (`release_id` identifies the pressing, not the copy). So this line isn't only a gate +fix; it's the identity tier-2 will need, bought now for free. + +**Scope kept honest:** index-only, no atlas bytes, no town cache, no goldens. `id` isn't read by the loader +(§7.3) — the dig/buy consumers and the gates read it, so no C code changes. **→ G:** re-emit with `sku_` +ids. **→ E:** assert the form (present, unique within the pack, `sku_`-prefixed for real-stock). + +*Process note:* F's six files were staged in the shared tree mid-flight; committed by explicit pathspec, so +F's session is untouched. --- diff --git a/docs/LANES/LANE_C_PUB.md b/docs/LANES/LANE_C_PUB.md index f116b8b..f1bc196 100644 --- a/docs/LANES/LANE_C_PUB.md +++ b/docs/LANES/LANE_C_PUB.md @@ -12,6 +12,9 @@ > held F's tag. §7 now states what `stockpack.js` actually implements, extended per-shop, and the > `type+base` cache keying is landed. **G's R23 atlas layout is ratified** (G re-emits provenance field > names only, §7.3). §0–§6 unchanged. +> **v5.0-alpha amendment (2026-07-17, R25 ledger #3):** **§7.2a added — slot ids must be sku-derived,** +> unique across packs and stable across re-emits (F's id-collision finding, made structural). Index-only; +> no atlas bytes, no goldens. §0–§6 unchanged. *The district ships **three** venue archetypes behind `?gigs=1` — `pub`, `band_room`, `rsl`. Lane A converts a chosen shop to a venue kind in place (`shop.type = kind`); C keys the interior recipe off @@ -257,6 +260,32 @@ The loader is `preloadStockPack(type, { base })` → it fetches **`stock_< - **`uv: [u0,v0,u1,v1]`, origin TOP-LEFT** — the loader flips V itself (`uvRect`). Do not pre-flip. - Read by the dig/buy consumers (not by the loader): **`id`, `title`, `artist`, `price`, `price_band`**. +### 7.2a Slot ids — unique across packs, stable across re-emits (v5.0-alpha, R25 ledger #3) +**Rule: `items[].id` MUST derive from the source's own stable key — never from position — and MUST be +unique across ALL packs.** For a real-stock (godverse) pack that key is the shop's POS sku: +**`id = "sku_" + `** (e.g. `sku_20260314070442`; G's snapshot already carries `sku` per item, it's +just dropped at emit). The shipped generic packs keep `rec_NNNN` as their namespace — **a real-stock pack +must not emit into it.** + +**Why — two reasons, both measured:** +1. **Positional ids collide by construction.** Both packs number from zero, so **all 120 godverse ids are + also generic-pack ids — 120/120 overlap (measured R25)**. An id-equality gate therefore cannot + discriminate which pack a rendered item came from: that is what made the brief's own #7a spec vacuous. + F caught it and replaced it with title-sets + texture-URL provenance (now the canonical honest-gate + pattern for stock). Sku ids make id-equality discriminating again — permanently, and while there are + two atlases to migrate instead of fifty. +2. **Position is not identity — and this one outlives the gate.** A positional id renumbers on every + re-bake: sell one record out of crate 550 and `rec_0050` silently becomes a *different* record. Anything + holding an id across snapshots — a wallet purchase, a saved dig, a gate assertion, and above all + **tier-2's sold-means-gone** — would then point at the wrong item. The POS sku is stable precisely + because it is the shop's own identity for that record: the same principle that makes `godverseShopId` + the POS shop id. *(`release_id` identifies the pressing, not the copy — the sku is the copy.)* + +**Scope: index-only.** `id` is not read by the loader (§7.3) — the dig/buy consumers and the gates read it. +**No atlas bytes, no town cache, no golden moves.** +**→ Lane E:** assert the form — `id` present, **unique within the pack**, and `sku_`-prefixed for +real-stock packs. + ### 7.3 Fields the runtime IGNORES — and the provenance ruling (the drift dies here) `version`, `atlas_px`, `cell`/`cell_w`/`cell_h`, `count`, `kind`, `tier`, `shop`, `crate`, `provenance` — **`stockpack.js` reads none of them.** So the authority for their shape is not C's