diff --git a/docs/LANES/LANE_E_NOTES.md b/docs/LANES/LANE_E_NOTES.md index bd34c95..0d6b5d5 100644 --- a/docs/LANES/LANE_E_NOTES.md +++ b/docs/LANES/LANE_E_NOTES.md @@ -1,5 +1,40 @@ # LANE E — cross-lane notes +## Round 26 wave 4 — the mint-id fence: my check was faithful, the rule was stale ⟨v5.0-beta⟩ + +**The red was mine and the fix is C's ruling.** My R25 slot-id check demanded `sku_` on every real-stock +pack — faithfully implementing C's R25 line, which was **written before mint existed**. It then failed all +14 of G's mint crates, whose ids honour its every intent: non-positional, seeded, stable, zero reuse. C's +ruling names it exactly: *"the rule was the stale thing, not E's check."* Fourth hold of the epoch, and +the fourth to trace to **a rule meeting a case written before the case existed.** + +**The fix is a fence, and it is STRONGER than the rule it replaces — that was the thing to prove.** The +easy green was to drop the prefix check. Instead the form is now sourcing-scoped per C §7.2a (R26 w4): +`sourcing:"real"` ⇒ `sku_` (a POS copy on a shelf) · `sourcing:"mint"` ⇒ `mint_` (a dealgod listing +standing in for one). **A crate wearing the other's prefix FAILS.** + +**Why this is a money bug and not a naming bug — G's judgment, now law.** G refused to file dealgod listing +ids under `sku_` to go green the easy way. They were right: **tier-2's sold-means-gone looks up POS skus.** +R24's id collision entered the *wrong shop*; the same collision in the money layer **sells a real record +out of a real shop to satisfy a minted stand-in.** The gate's error says that, in those words — the cost, +not the rule. + +**Proved 7/7 — that the fence bites, not merely that the red stopped.** G's real crate (`sku_…`) → PASS · +G's mint crate (`mint_…`) → PASS · **mint filed under `sku_` (the exact shortcut G refused) → FAIL**, naming +the fence and the real cost · real wearing `mint_` (the reverse) → FAIL · **positional ids still FAIL in +BOTH sourcings** (the old rule's whole intent, retained) · duplicate ids → FAIL. Loosening would have +passed the first two and lost the last four; this passes the first two and keeps all four. + +**Both older suites brought forward rather than left to rot.** The R26 suite's mint fixture wore `sku_` +ids, so the fence correctly rejected it — a fixture that fails for a *new* reason has stopped testing what +it was written for. Moved to the `mint_` space: **17/17**. R25 licence suite: **8/8**, tally still firing +**via the tally**. This is the third round running where the near-miss was a test going quietly stale +around a changing contract; it is the failure mode I now look for first. + +**Gates:** `atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s)` (1 real + +14 mint, every one showing its flag to a human) · `validate_manifest` exit 0 · **`qa.sh --strict --matrix` +7 passed / 0 failed / 0 warn — QA GREEN.** F's 6/1/0 is now 7/0/0. **→ F: the beta is yours to tag.** + ## Round 26 — the sourcing arm + the atlas manifest ⟨v5.0-beta⟩ **1. The field is `sourcing`, NOT `tier` — and reading C's contract instead of the brief is the only diff --git a/pipeline/validate_atlas.py b/pipeline/validate_atlas.py index 159ad5e..9a6b426 100644 --- a/pipeline/validate_atlas.py +++ b/pipeline/validate_atlas.py @@ -70,7 +70,16 @@ REQUIRED_ITEM = ("id", "title", "artist", "price", "price_band", "atlas", "uv") # C §7.2a (R25 #3): slot ids derive from the source's stable key, never position, and are unique across # ALL packs. A real-stock pack's key is the shop's POS sku → `sku_`; the shipped generic packs own # the `rec_`/`boo_`/`toy_` positional namespace and a real-stock pack must not emit into it. -SLOT_ID_PREFIX = "sku_" +# C §7.2a as amended (R26 wave 4) — THE PREFIX IS A NAMESPACE FENCE, and the id form is SOURCING-SCOPED. +# My R25 check demanded `sku_` on every real-stock pack. C wrote that rule before mint existed, so it read +# as one space when there are two — and it failed 14 legitimate mint crates whose ids honour its every +# intent (non-positional, seeded, stable, zero reuse). C's ruling: the rule was the stale thing, not the +# check. The fence is not decoration: `sku_` names a POS copy on a shelf, `mint_` names a dealgod listing +# standing in for one. G refused to file listing ids under `sku_` to go green the easy way, and that +# judgment is the law now — tier-2's sold-means-gone looks up POS skus, so a prefix that lies doesn't fail +# a gate, it SELLS THE WRONG RECORD out of a real shop to satisfy a minted stand-in. Never treat them as +# one space (same standing note as plan-ids vs godverse-ids). +SLOT_ID_PREFIX = {"real": "sku_", "mint": "mint_"} GENERIC_PACKS = os.path.join(ROOT, "web", "assets", "models", "stock_*_index.json") # C §7.2b (R26 #1): `sourcing` — real (that shop's own POS snapshot) vs mint (a seeded sample standing in # for a keyed shop with no POS). NOTE the brief called this `tier`; C ruled it CANNOT be — `tier` is already @@ -266,13 +275,31 @@ def validate_index(path): # ── C §7.2a: the slot-id form. Aggregated, not per-item: 120 identical errors bury the one that # matters. Uniqueness WITHIN the pack is the dup check above; these two are the cross-pack half. if ids: - pos = sorted(i for i in ids if not str(i).startswith(SLOT_ID_PREFIX)) - if pos: - errs.append(f"{name}: {len(pos)}/{len(ids)} slot id(s) are not `{SLOT_ID_PREFIX}`-prefixed " - f"(e.g. {pos[:3]}) — C §7.2a: a real-stock id derives from the shop's POS sku, never " - f"from position. Positional ids renumber on every re-bake, so anything holding one " - f"across snapshots (a wallet purchase, tier-2's sold-means-gone) points at a " - f"different record.") + # The fence can only be checked once the space is known. An unknown `sourcing` already FAILS above + # (§7.2b), so this doesn't leak a pass — but it must not silently half-run either: no space, no + # verdict on which prefix belongs. + want = SLOT_ID_PREFIX.get(sourcing) + if want: + wrong = sorted(i for i in ids if not str(i).startswith(want)) + if wrong: + # Name the fence-crossing specifically when the crate wears the OTHER space's prefix — that + # is a different (and worse) fault than a merely positional id, and the message should say + # which one it is rather than make the reader guess. + other = {p for s, p in SLOT_ID_PREFIX.items() if s != sourcing} + crossed = sorted(i for i in wrong if any(str(i).startswith(p) for p in other)) + if crossed: + errs.append(f"{name}: sourcing={sourcing!r} but {len(crossed)}/{len(ids)} slot id(s) wear " + f"another space's prefix (e.g. {crossed[:3]}) — C §7.2a (R26 w4): prefixes are " + f"NAMESPACE FENCES. `sku_` is a POS copy on a shelf; `mint_` is a dealgod " + f"listing standing in for one. A crate wearing the other's prefix is not a " + f"naming slip: tier-2's sold-means-gone looks up POS skus, so this sells a " + f"real record out of a real shop to satisfy a minted stand-in.") + else: + errs.append(f"{name}: sourcing={sourcing!r} ⇒ ids must be `{want}`-prefixed, but " + f"{len(wrong)}/{len(ids)} are not (e.g. {wrong[:3]}) — C §7.2a: an id derives " + f"from its source's stable key, never from position. Positional ids renumber " + f"on every re-bake, so anything holding one across snapshots (a wallet " + f"purchase, tier-2's sold-means-gone) points at a different record.") gen = generic_pack_ids() if not gen: # the vacuous-gate law: if the other side of "unique across ALL packs" isn't here, say so —