Lane A round 14: v3.0 release — 400-seed district invariant sweep + CITY_SPEC §v3 frozen
Release round: harden + freeze, no new systems. No golden moved (synthetic 0x3fa36874, gig
0x1f636349 both re-verified frozen); selfcheck.js is the only code touched, so no seeded generation
stream gained a draw (release-round law).
- selfcheck.js: factored the district checks out of gigSuite into districtInvariants(plan,label)
(behaviour-preserving — hero-seed gigSuite runs byte-identical), then added a 400-seed synthetic
sweep + the large edge seeds asserting the full district contract each (2-4 venues, >=1 pub, never
the openLate shop, no band twice/night town-wide, per-venue 4-6 nights, every poster off the
carriageway). Asserts the seeded count spans {2,3,4} across 400 seeds (146/121/133 — range is
exercised, not pinned). osm graceful placement: katoomba (19 shops, no warehouse district) still
lands 2-4 valid venues incl. a pub for every hero seed. Poster check kept at roadWidth/2 (a literal
poleOffset compare would false-fail corner posters); added -1e-6 epsilon on the strict boundary.
ALL GREEN 14264/14264 in ~0.7s.
- CITY_SPEC §v3: final wording pass, marked FROZEN at v3.0 (round 14) with a v2-style marker + an
explicit carve-out for F's pending alias-line deletion. Fixed the roadWidth prose (full carriageway
width, not 'half-corridor'). Genres final (pubrock/grunge/covers).
Coordination: the R12 single-venue alias line stays in §v3 for F to delete atomically with the
gig_state.js getters (debt #1) — deleting now would be doc-ahead-of-code (alias still live) and race F.
Flagged to F that the runtime block omits nightOf(id); to B that venue.js:228 is a 2nd bare-string
site. Verified: mutation-tested the sweep (non-vacuous — catches 5-venue/7-night/on-road violations)
+ adversarial pass (goldens frozen, freeze matches code, deterministic).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
55f6698fb2
commit
a3911a4590
@ -1,9 +1,39 @@
|
||||
# LANE A — CITYGEN · progress (PROCITY-A)
|
||||
|
||||
*Status: **all deliverables landed; self-check all-green (3273/3273); rounds 2–13 closed** (see below).
|
||||
Node QA gates (selfcheck · scaffold · consistency) GREEN. v3.0-beta DISTRICT interface published (2–4
|
||||
venues, week schedule, town-wide posters); gig golden `0x1f636349`. Strict 6/6 is F's end-of-round gate.*
|
||||
*Date: 2026-07-15 · owner: PROCITY-A (Opus 4.8)*
|
||||
*Status: **all deliverables landed; self-check all-green (14264/14264); rounds 2–14 closed** (see below).
|
||||
v3.0 RELEASE round: 400-seed district invariant sweep landed + CITY_SPEC §v3 frozen at `v3.0`. Goldens
|
||||
unchanged (synthetic `0x3fa36874`, gig `0x1f636349`) — no golden moved, no rng draw added this round.*
|
||||
*Date: 2026-07-16 · owner: PROCITY-A (Opus 4.8)*
|
||||
|
||||
## Round 14 (2026-07-16) — v3.0 RELEASE: invariant sweep + CITY_SPEC §v3 freeze
|
||||
|
||||
Release round, Lane A scope = harden + freeze (no new systems). **No golden moved** (synthetic
|
||||
`0x3fa36874`, gig `0x1f636349` both re-verified frozen); the only code touched is `selfcheck.js`
|
||||
(test-only), so no seeded stream gained a draw — the release-round law holds.
|
||||
|
||||
Delivered:
|
||||
1. **400-seed district invariant sweep** (`selfcheck.js`, ALL GREEN **14264/14264** in ~0.7s). Factored
|
||||
the district checks out of `gigSuite` into `districtInvariants(plan,label)` (behaviour-preserving — the
|
||||
4 hero-seed gigSuite runs are byte-identical), then swept **seeds 1–400 + the large edge seeds**
|
||||
asserting the whole contract each: 2–4 venues, ≥1 pub, never the openLate shop, no band twice per night
|
||||
town-wide, per-venue 4–6 nights, every poster off the carriageway. Added a distribution assert that the
|
||||
seeded count **spans {2,3,4}** across 400 seeds (146/121/133) — proves the range is exercised, not pinned.
|
||||
`structuralSuite` still runs on the 6 hero SEEDS + osm parity (unchanged); the sweep runs the light
|
||||
district checks only, so 400 gig-on generations stay ~0.5s.
|
||||
2. **osm graceful placement** in the sweep: `katoomba` (19 shops, **no warehouse district**) still lands
|
||||
2–4 valid venues incl. a pub for every hero seed — proving `pickVenues`' off-spine fallback. Honest
|
||||
scope noted: both osm fixtures keep a main spine, so this proves the no-warehouse branch (not no-spine).
|
||||
3. **Poster-check hardening**: kept the `roadWidth/2` nearest-edge formulation (a literal `poleOffset`
|
||||
compare would false-fail corner posters); added a `−1e-6` epsilon on the strict `<` to immunise the
|
||||
exact kerb-line boundary against r2 rounding (only reachable on a no-main-spine fallback; not hit today).
|
||||
4. **CITY_SPEC §v3 frozen at `v3.0`** — §v2-style FROZEN marker, final wording pass over venues table,
|
||||
genre map, gigKey contract, corridor law, gig golden. Genres final (pubrock/grunge/covers, Fable's ruling).
|
||||
|
||||
Coordination: the R12 single-venue **alias line stays in §v3 for F to delete atomically** with the
|
||||
`gig_state.js` getters (debt #1) — deleting it now would be doc-ahead-of-code (alias still live) and would
|
||||
race F on the same lines; the freeze marker carves it out explicitly. Flagged to F that the runtime block
|
||||
omits `nightOf(id)`, and to B that `venue.js:228` is a second bare-string edit site. **Not touched:**
|
||||
`web/assets/manifest.json` shows a concurrent Lane E `glb_law` edit in the shared tree — left for E.
|
||||
|
||||
## Round 13 (2026-07-15) — v3.0-beta THE DISTRICT: 2–4 venues + the week + town-wide posters + verge fix
|
||||
|
||||
|
||||
@ -204,7 +204,15 @@ Interiors are **not** street geometry. Walking into a doorway triggers
|
||||
exactly where you stood. Street windows show a cheap fake (tinted glass + emissive warm glow at
|
||||
night in v1; interior-mapping shader is a stretch goal).
|
||||
|
||||
## CityPlan v3 — gig layer (`?gigs=1`) — v3.0-beta (THE DISTRICT)
|
||||
## CityPlan v3 — gig layer (`?gigs=1`) — THE DISTRICT
|
||||
|
||||
> **FROZEN at `v3.0` (round 14).** The authoritative Layer-1 **gig-layer** contract — venues, the week's
|
||||
> schedule, posters, the `gigKey` map, the corridor law, and the runtime surface. `web/js/citygen/selfcheck.js`
|
||||
> enforces every law here, including the round-14 **400-seed district invariant sweep** + an osm
|
||||
> graceful-placement case. Changes are a **CITY_SPEC amendment in the same commit + a gig-golden re-pin**,
|
||||
> never silent drift. **One edit is still pending and lands with Lane F's alias-retirement commit** (R13
|
||||
> debt #1): the R12 single-venue compat alias in the runtime block below retires; everything else here is
|
||||
> final for the `v3.0` tag.
|
||||
|
||||
> **Prime flag law.** The gig layer is a **post-hoc augmentation** applied by the selector only when
|
||||
> `?gigs=1`. With the flag OFF, `generatePlanFor(seed, src)` returns the byte-identical v2 base plan —
|
||||
@ -281,7 +289,8 @@ places E's poster skin at `(x, z)` rotated `ry`; F wires "poster → that venue'
|
||||
the **full corridor** (carriageway + verge/footpath, centreline ± width/2), never the carriageway alone —
|
||||
so a naive `node.x + 2` seats furniture mid-bitumen. `web/js/core/registry.js` now exports the split as
|
||||
pure functions over an edge (no plan fields change ⇒ base goldens stay frozen), re-exported from the
|
||||
barrel: **`roadWidth(edge)`** (carriageway half-corridor: main 10 m, side 6–8, lane = all, arcade 0),
|
||||
barrel: **`roadWidth(edge)`** (full carriageway width — main 10 m, side 6–8, lane = all, arcade 0; the
|
||||
kerb sits at `roadWidth/2` from the centreline),
|
||||
**`vergeBand(edge) → [inner,outer]`** (where footpath furniture lives), **`poleOffset(edge)`** (where a
|
||||
pole/poster seats — just behind the kerb). Posters, poles, verandah posts belong in the verge band; the
|
||||
selfcheck asserts no poster stands within `roadWidth/2` of any edge centreline.
|
||||
|
||||
@ -2,6 +2,47 @@
|
||||
|
||||
Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`.
|
||||
|
||||
## Round 14 (2026-07-16) — v3.0 RELEASE: invariant sweep + CITY_SPEC §v3 frozen
|
||||
|
||||
Release round — no new systems, no golden moves. **Synthetic `0x3fa36874` and gig `0x1f636349` are
|
||||
unchanged**; the only code I touched is `selfcheck.js` (test-only), so no seeded stream gained a draw.
|
||||
|
||||
### The 400-seed district sweep (`selfcheck.js`, ALL GREEN 14264/14264 in ~0.7s)
|
||||
- Factored the district-contract checks out of `gigSuite` into `districtInvariants(plan, label)` so the
|
||||
sweep asserts them at SCALE without re-running the heavy `structuralSuite` (that still runs on the 6
|
||||
hero `SEEDS` + osm parity). Behaviour-preserving — the 4 hero-seed `gigSuite` runs are byte-identical.
|
||||
- Sweeps **seeds 1–400** synthetic + the large edge seeds (2e9, 8675309) asserting the full district
|
||||
contract each: 2–4 venues, ≥1 pub, never the openLate shop, no band twice in one night town-wide,
|
||||
per-venue 4–6 nights, every poster off the carriageway. Also asserts the seeded count **spans {2,3,4}**
|
||||
across the range (measured 146/121/133) — proves the 2–4 range is genuinely exercised, not pinned.
|
||||
- **osm graceful placement**: `katoomba` (smallest fixture, 19 shops, **no warehouse district**) still
|
||||
lands 2–4 valid venues incl. a pub for every hero seed — proving `pickVenues`' off-spine fallback.
|
||||
Honest scope: both osm fixtures keep a main spine, so this proves the *no-warehouse* branch, not
|
||||
*no-spine* (a genuine no-spine town would need a single-row fixture that doesn't exist yet — v3.1).
|
||||
- Poster check kept as "≥ `roadWidth/2` from the nearest edge centreline" (not a literal `poleOffset`
|
||||
compare, which would false-fail legit corner posters seated behind their placement edge's `poleOffset`
|
||||
but inside a crossing edge's clearance band). Added a `−1e-6` epsilon on the strict `<` to immunise the
|
||||
exact kerb-line boundary against r2 rounding (only reachable on a no-main-spine fallback; not hit today).
|
||||
|
||||
### CITY_SPEC §v3 frozen at v3.0
|
||||
Final wording pass; marked **FROZEN at `v3.0` (round 14)** with a §v2-style marker. Venues table, genre
|
||||
map, gigKey contract, corridor law, and gig golden `0x1f636349` are all final (genres are pubrock/grunge/
|
||||
covers — Fable's R14 ruling, John did not re-flavour).
|
||||
|
||||
### → Lane F: the alias-retirement handshake (R13 debt #1) — I deliberately did NOT touch it
|
||||
The R12 single-venue compat alias is **still live** (`gig_state.js:141-151`, read by `audio.js`/`venue.js`),
|
||||
so I left the §v3 alias sentences (`CITY_SPEC.md` ~lines 271–272) **intact** — deleting them at freeze
|
||||
time would be doc-ahead-of-code and would race you on the same lines. The freeze marker explicitly carves
|
||||
this out as your pending edit. In your alias-retirement commit (after B drops its readers), delete both
|
||||
`gig_state.js:141-151` and those two spec sentences **together** — one atomic step, one deleter, no race.
|
||||
- While you're in that runtime block: it enumerates `stateOf/onOf/openOf/gigOf/coverOf/bandNameOf/paidOf`
|
||||
+ `markPaidOf` + `.venueShopIds`, but **omits `nightOf(id)`** (`gig_state.js:131`). Add it when you edit.
|
||||
|
||||
### → Lane B: second edit site for the bare-string arm
|
||||
When you remove the `venue.update()` bare-string alias arm, note `venue.js:228` also self-calls
|
||||
`update('quiet')` at construction through that same arm — fix both or it falls through (harmless today:
|
||||
still yields `'quiet'`, but it's a live second site).
|
||||
|
||||
## Round 13 (2026-07-15) — v3.0-beta THE DISTRICT (published FIRST; C/D/B/F hang off this)
|
||||
|
||||
Supersedes the R12 alpha interface below. Same flag (`?gigs=1`), same prime-flag law: flag OFF ⇒ plan
|
||||
|
||||
@ -145,7 +145,13 @@ function structuralSuite(plan, label) {
|
||||
// The v3 GIG layer (?gigs=1). Runs the full structuralSuite (venue-aware) plus gig-specific
|
||||
// invariants. Validates the DISTRICT interface every downstream lane (C/D/B/F) hangs off this round.
|
||||
function gigSuite(plan, label) {
|
||||
structuralSuite(plan, label); // full coverage; venues exempt from late-shop rule
|
||||
structuralSuite(plan, label); // full structural coverage; venues exempt from late-shop rule
|
||||
districtInvariants(plan, label); // + the v3 district contract (also run standalone by the ROUND14 sweep)
|
||||
}
|
||||
|
||||
// The v3 DISTRICT contract, factored out of gigSuite (ROUND14) so the 400-seed sweep below can assert it
|
||||
// at SCALE without re-running the heavy structuralSuite (already covered on the 6 SEEDS + osm parity).
|
||||
function districtInvariants(plan, label) {
|
||||
const venues = plan.shops.filter(s => s.venue);
|
||||
const byId = new Map(venues.map(v => [v.id, v]));
|
||||
|
||||
@ -199,7 +205,9 @@ function gigSuite(plan, label) {
|
||||
const d = Math.hypot(p.x - nx, p.z - nz);
|
||||
if (d < best) { best = d; bestEdge = e; }
|
||||
}
|
||||
if (bestEdge && best < roadWidth(bestEdge) / 2) { onRoad = [p.id, bestEdge.id, +best.toFixed(2)]; break; }
|
||||
// −1e-6 immunises the exact kerb-line boundary against r2 coord rounding (only reachable on a
|
||||
// no-main-spine fallback where poleOffset == roadWidth/2; never hit by the current sweep).
|
||||
if (bestEdge && best < roadWidth(bestEdge) / 2 - 1e-6) { onRoad = [p.id, bestEdge.id, +best.toFixed(2)]; break; }
|
||||
}
|
||||
ok(onRoad === null, `${label}: no poster stands on a carriageway` + (onRoad ? ` (poster ${onRoad[0]} is ${onRoad[2]}m from edge ${onRoad[1]}'s centreline)` : ''));
|
||||
}
|
||||
@ -294,6 +302,35 @@ const GIG_GOLDEN = 0x1f636349; // ROUND13 re-pin: multi-venue district changed t
|
||||
ok(GIG_GOLDEN !== 0 && hash === (GIG_GOLDEN >>> 0), `gig layer golden 0x${hash.toString(16)} matches pinned 0x${(GIG_GOLDEN >>> 0).toString(16)}`);
|
||||
}
|
||||
|
||||
// ── 3d. district invariant sweep (ROUND14): 400 synthetic seeds + osm graceful placement ───────────
|
||||
// Release-round Lane A gate: the district contract must hold at SCALE, not just the 4 hero seeds. Runs
|
||||
// districtInvariants ONLY (structuralSuite already covers the 6 SEEDS + osm parity) so 400 full gig-on
|
||||
// generations stay well under a second. Pure validation — NO new rng draws, no golden moves.
|
||||
section('district invariant sweep (ROUND14: 400 synthetic seeds + osm graceful placement)');
|
||||
const SWEEP_N = 400;
|
||||
const sweepVenueCounts = new Set();
|
||||
for (let s = 1; s <= SWEEP_N; s++) {
|
||||
const plan = generatePlanFor(s, 'synthetic', { gigs: true });
|
||||
districtInvariants(plan, `sweep syn ${s}`);
|
||||
sweepVenueCounts.add(plan.shops.filter(v => v.venue).length);
|
||||
}
|
||||
// also cover the large/edge uint32 seeds (2e9, 8675309 …) — where rng edge cases hide — that fall
|
||||
// outside 1..400 and otherwise only get the flags-off byte-identity check, not the district contract.
|
||||
for (const s of SEEDS) if (s > SWEEP_N) districtInvariants(generatePlanFor(s, 'synthetic', { gigs: true }), `sweep syn ${s}`);
|
||||
// the seeded 2–4 count is genuinely exercised across the range, not pinned to one value
|
||||
ok([2, 3, 4].every(n => sweepVenueCounts.has(n)),
|
||||
`sweep: venue count spans {2,3,4} across ${SWEEP_N} seeds (saw {${[...sweepVenueCounts].sort((a, b) => a - b).join(',')}})`);
|
||||
|
||||
// osm graceful placement: katoomba is the smallest fixture (19 shops) with NO warehouse district, so
|
||||
// pickVenues' band_room/rsl rules hit their off-spine fallback — yet still land 2–4 valid venues incl.
|
||||
// a pub. (Both osm fixtures keep a main spine, so this proves the no-warehouse branch, not no-spine.)
|
||||
for (const seed of GIG_SEEDS) {
|
||||
const plan = generatePlanFor(seed, 'osm', { gigs: true, town: 'katoomba' });
|
||||
ok(plan.blocks.every(b => b.kind !== 'warehouse'),
|
||||
`sweep osm/katoomba ${seed}: fixture has no warehouse district (graceful-placement precondition)`);
|
||||
districtInvariants(plan, `sweep osm/katoomba ${seed}`);
|
||||
}
|
||||
|
||||
// ── 4. every facade skin referenced by the registry exists on disk ──────────────────
|
||||
section('assets on disk');
|
||||
for (const f of allFacadeSkins()) ok(existsSync(join(ASSETS, f)), `registry facade exists: ${f}`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user