SEAM RULING (§8): bless the direct wire — NO lifecycle hook. F already holds currentAdapter (interior_mode.js:67) and passes it to dig.open() PER DIG (:111), so the live read lands, F updates the pack, the next riffle picks it up; not-landed/slow/dead => the adapter is simply the tier-1 pack => the dig never blocks (charter risk #1). No C lifecycle change. BUT THE QUESTION UNDERNEATH IT IS C'S, AND THE OBVIOUS WIRING IS WRONG. pickRealOffers indexes pack.items[(rr()*len)|0], so FILTERING THE ARRAY RESHUFFLES THE CRATE. Measured, 5 crates: one record selling changed 21 OTHER records (6/2/2/6/5 collateral). Pick-then-omit (draw from the stable full list, drop gone after) = 0 collateral: a crate with 3 sold shows 13, not a different 16. Selling one record must remove one record, not re-stock the shop. THE TRAP: both forms hide the sold record — so F's planned assertion ("a real gone item never renders") GOES GREEN ON THE WRONG WIRING. It cannot see the 21. -> F: assert on COLLATERAL (the survivors are unchanged across a gone delta), not absence. Vacuous-gate law, third application, and the cheapest to miss because it passes. LANDED (dig.js): pack.gone (Set|array) omitted at PICK time. F's invariant published: update item FIELDS in place (live price) freely; NEVER add/remove/reorder items[] — that identity+order IS the seeded pick's stability (R25's positional-id lesson, one layer up). DETERMINISM BOUNDARY sits exactly here: server down => byte-identical tier 1 (gate-able); server up => the crate legitimately changes (tier 2 is non-deterministic BY DESIGN), so no gate may assert dig determinism with the server up, and pack.gone never enters a seeded stream or golden. SCOPED: the bin fan is decorative (a different seeded pick from the dig's offers) — a sold cover may linger there until rebuild. That's when I'd publish a post-build hook; the epoch's claim doesn't need it, because the crate you RIFFLE is true. VERIFIED: pure-function measurement for the collateral claim, then end-to-end through the real dig's public surface (pull -> Esc -> scroll -> pull): sold record gone, survivors 4/4, collateral 0, items[] untouched at 120, live in-place price shows on the next pull. (My first harness returned the same title 4x — I never un-pulled — so its number was an artifact; re-ran it properly rather than report a metric I'd just watched lie.) DOCS FREEZE (§6): LANE_C_PUB is v5.0-FROZEN, whole document, with the case law at the top: three amendments here exist because a rule of mine met a case written AFTER it, and both times the lane building to my rule took the red. Header now states it: when a line meets a case it didn't foresee, the line is the stale thing — amend it, don't bend the case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
01f398960b
commit
6960bdd279
@ -7,6 +7,51 @@ Last updated: 2026-07-17 (round 26) · owner: PROCITY-C · reviewer: Fable
|
||||
|
||||
---
|
||||
|
||||
## Update 2026-07-17 (round 27, v5.0 THE LIVE CRATE) — the tier-2 seam + LANE_C_PUB v5-FROZEN
|
||||
|
||||
R27 §Lane C (ledgers #2 + #6). Wave 1 — ran after F tagged `v5.0-beta` (`78848ed`, 7/0/0).
|
||||
|
||||
### The seam ruling (§8): bless the direct wire — but the *form* was the real question
|
||||
Fable asked: publish a post-build hook, or bless the direct wire? **Answer: bless it — no lifecycle hook.**
|
||||
F already holds `currentAdapter` (`interior_mode.js:67`) and passes it to `dig.open()` **per dig** (`:111`),
|
||||
so the live read lands, F updates the pack, and the next riffle picks it up. Read not landed / slow / dead ⇒
|
||||
the adapter is simply the tier-1 pack ⇒ the dig never blocks. No C lifecycle change.
|
||||
|
||||
**But the question underneath it — *how* `gone` applies — is C's, and the obvious wiring is wrong.**
|
||||
`pickRealOffers` indexes `pack.items[(rr()*len)|0]`, so **filtering the array reshuffles the crate**.
|
||||
Measured across 5 crates: **one record selling changed 21 OTHER records** (6/2/2/6/5 collateral). Pick-then-
|
||||
omit — draw from the stable full list, drop gone *after* — has **0 collateral**: a crate with 3 sold shows
|
||||
13, not a different 16. Selling one record must remove one record, not re-stock the shop.
|
||||
|
||||
**And the trap: both forms hide the sold record.** So F's planned sandbox assertion — "a real `gone` item
|
||||
never renders" — **goes green on the wrong wiring**. It cannot see the 21. → **F: assert on collateral (the
|
||||
survivors are unchanged across a `gone` delta), not absence.** That's the vacuous-gate law's third
|
||||
application, and the cheapest to miss, because it passes.
|
||||
|
||||
**Landed** (`dig.js`): `pack.gone` (Set|array) omitted at pick time; **F's invariant published** — update
|
||||
item *fields* in place (live price) freely, **never add/remove/reorder `items[]`**; that identity+order IS
|
||||
the seeded pick's stability (the same lesson as R25's positional ids, one layer up).
|
||||
**Determinism boundary sits exactly here:** server down ⇒ byte-identical tier 1 (gate-able); server up ⇒ the
|
||||
crate legitimately changes — tier 2 is non-deterministic *by design*, so **no gate may assert dig determinism
|
||||
with the server up**, and `pack.gone` never enters a seeded stream or a golden.
|
||||
**Scoped honestly:** the bin fan is decorative (a different seeded pick from the dig's offers), so a sold
|
||||
cover may linger there until rebuild — that's when I'd publish a post-build hook, and the epoch's claim
|
||||
doesn't need it, because *the crate you riffle is true*.
|
||||
|
||||
**Verified** — pure-function measurement for the collateral claim, then end-to-end through the real dig's
|
||||
public surface (pull → Esc → scroll → pull): sold record gone · **survivors 4/4 · collateral 0** · `items[]`
|
||||
untouched at 120 · live in-place price shows on the next pull. *My first dig harness returned the same title
|
||||
4× (I never un-pulled), so its "survivors" number was a harness artifact — I re-ran it properly rather than
|
||||
report a metric I'd just watched lie.*
|
||||
|
||||
### Docs freeze (§6): LANE_C_PUB is **v5.0-FROZEN**, whole document
|
||||
Marked, with the case law kept at the top rather than buried: three of this doc's amendments exist because a
|
||||
rule of mine met a case written *after* it (§7's naming vs its own loader; §7.2a's `sku_` before mint) — and
|
||||
both times the lane building to my rule took the red. The header now says it plainly: **when a line here
|
||||
meets a case it didn't foresee, the line is the stale thing — amend it, don't bend the case.**
|
||||
|
||||
---
|
||||
|
||||
## Update 2026-07-17 (round 26 wave 4) — the mint-id fence: my rule was the stale thing
|
||||
|
||||
F's fourth hold traced to **my** §7.2a. I wrote "`sku_`-prefixed for real-stock packs" in R25 — **before mint
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
# LANE C — interiors contract (venues + real-town class mapping) → Lanes A/D/E/F
|
||||
# LANE C — interiors contract (venues · real-town mapping · real stock) → Lanes A/D/E/F/G
|
||||
|
||||
> **v5.0-FROZEN · 2026-07-17 · the whole document.** Every section below is the shipped, measured state of
|
||||
> Lane C's contracts across five epochs. Changes from here are version-bumped amendments, recorded in the
|
||||
> marker list — the same discipline §0–§5 have carried since v3.0. **Case law worth keeping:** three of this
|
||||
> doc's amendments exist because a rule of mine met a case written *after* it (§7's naming vs the loader,
|
||||
> §7.2a's `sku_` before mint existed) — the lane building to the rule took the red both times. When a line
|
||||
> here meets a case it didn't foresee, **the line is the stale thing.** Amend it; don't bend the case.
|
||||
|
||||
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane D + Lane F. §0–§5 describe the *shipped*
|
||||
> venue state; changes require a version bump (CITY_SPEC amendment law). Verified R13 (build) + R14 (audit).
|
||||
@ -413,3 +420,45 @@ Fresh context, against G's merged `stock_godverse/3962749/` (120 real records):
|
||||
- **Collision fixed:** shop pack (120 items) and town pack (350) coexist as distinct objects.
|
||||
- **Fail-soft with a POPULATED cache:** missing base ⇒ `null` ⇒ parody (the R23 bug is gone).
|
||||
- **Ceilings:** 98 draws with real covers via `opts.stockBase`; `pathOK`; 0 carves; 0 console errors.
|
||||
|
||||
---
|
||||
|
||||
## 8. Tier-2 live enrichment — the seam (v5.0, R27 ledger #2) → Lane F
|
||||
|
||||
**Ruling: bless the direct wire — no new lifecycle hook. But the `gone` *application form* is C's, and it's
|
||||
landed in `dig.js`.** The question wasn't hook-vs-no-hook; it was *how* `gone` applies.
|
||||
|
||||
**1. Lifecycle — no hook needed.** F already holds `currentAdapter` (`interior_mode.js:67`) and passes it to
|
||||
`dig.open()` **per dig** (`:111`) — a fresh read every time a crate is opened. When the live read lands, F
|
||||
updates the pack; the next dig picks it up. If the read hasn't landed, hasn't finished, or failed, the
|
||||
adapter is simply the tier-1 pack — **the dig never blocks on the network** (charter risk #1) and no C
|
||||
lifecycle changes.
|
||||
|
||||
**2. The form — pick-then-omit, NEVER filter-the-array.**
|
||||
`pack.gone` = a `Set`/array of ids the live read reports sold. C picks from the **full, stable `items[]`**
|
||||
and omits gone ids **after** the draw.
|
||||
- **Why (measured, R27, 5 crates):** filtering `items[]` first **reshuffles the crate** — every pick indexes
|
||||
by position, so **one sale changed 21 OTHER records**. Pick-then-omit: **0 collateral**. A crate with 3
|
||||
sold shows 13, not a different 16.
|
||||
- **Both forms hide the sold record** — so **“the gone item never renders” cannot tell them apart.** → **F:
|
||||
the sandbox proof (#4) must assert on *collateral* (the survivors are unchanged across a `gone` delta),
|
||||
not just absence. The obvious assertion is vacuous here.** (Vacuous-gate law, third application — and the
|
||||
cheapest one to get wrong, because it goes green.)
|
||||
- **Proven in the real dig:** sold record gone · survivors **4/4** kept · **collateral 0** · `items[]`
|
||||
untouched (120).
|
||||
|
||||
**3. F's invariant — `items[]` identity and order ARE the pick's stability.**
|
||||
- **Do:** update item *fields* in place (live `price`) — free, no reshuffle; the card reads `it.price` at
|
||||
pull time, so a live price shows on the next pull.
|
||||
- **Never:** add, remove, or reorder `items[]`. That is what `pack.gone` exists for.
|
||||
|
||||
**4. The determinism boundary sits exactly here.** Server down ⇒ no `gone`, no live fields ⇒ the dig is
|
||||
tier 1, deterministic and gate-able. Server up ⇒ the crate legitimately changes as records sell — tier 2 is
|
||||
non-deterministic **by design** (charter law #2). So **no gate may assert dig determinism with the server
|
||||
up**, and no live value may enter a seeded stream or a golden: `pack.gone` is consumed at pick time and
|
||||
never persisted.
|
||||
|
||||
**5. The bin fan is decorative, not inventory.** The room's visible sleeves are built at `buildInterior` from
|
||||
a *different* seeded pick than the dig's offers — a fan, not the crate's contents. A sold record's cover may
|
||||
linger in the fan until the room rebuilds. If we ever want the fan to honour `gone`, **that** is when I'd
|
||||
publish a post-build hook; it is not needed for the epoch's claim, because the crate you *riffle* is true.
|
||||
|
||||
@ -286,10 +286,21 @@ export function createDig(THREE, renderer) {
|
||||
document.addEventListener('keydown', onKey, true);
|
||||
|
||||
// ?stock=real: seeded pick of real pack items (same bin seed → same crate contents).
|
||||
// TIER-2 SEAM (v5.0): `pack.gone` = ids the live read says sold IN THE REAL SHOP. We pick from the
|
||||
// FULL, stable `pack.items` and omit gone ids AFTER the draw — never from a filtered array.
|
||||
// Measured (R27, 5 crates): filtering the array first reshuffles the crate — one sale changed 21 OTHER
|
||||
// records, because every pick indexes by position. Pick-then-omit: 0 collateral, the sold record simply
|
||||
// leaves and the survivors keep their slots (a crate with 3 sold shows 13, not a different 16).
|
||||
// Both forms hide the sold record, so "the gone item never renders" cannot tell them apart — the
|
||||
// collateral is what a gate must assert on. F: update item FIELDS in place (live price) freely; do NOT
|
||||
// add, remove, or reorder `pack.items` — that identity/order IS the seeded pick's stability.
|
||||
function pickRealOffers(pack, seed, n) {
|
||||
const rr = mulberry32(seed >>> 0), out = [];
|
||||
const gone = pack.gone instanceof Set ? pack.gone
|
||||
: (Array.isArray(pack.gone) ? new Set(pack.gone) : null);
|
||||
for (let i = 0; i < n; i++) {
|
||||
const it = pack.items[(rr() * pack.items.length) | 0];
|
||||
if (gone && gone.has(it.id)) continue; // sold in the real shop → this slot is simply empty
|
||||
// Real fields straight off the atlas index (§7.2c): condition/sleeve_cond are OPTIONAL — present on
|
||||
// POS-sourced items, absent on mint ones that had no genuine grade. `y` (year) is never in the index,
|
||||
// so it stays empty and the card simply omits it. No invented values: an unstated grade stays unstated.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user