Lane C R25: sku-derived slot ids — F's id-collision finding made structural (ledger #3)

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_" + <POS 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 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-17 02:18:37 +10:00
parent 2c1ae37160
commit 0d8d59a22a
2 changed files with 62 additions and 1 deletions

View File

@ -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_" + <POS 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.
---

View File

@ -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 **`<base>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_" + <POS 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