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)
|
# LANE A — CITYGEN · progress (PROCITY-A)
|
||||||
|
|
||||||
*Status: **all deliverables landed; self-check all-green (3273/3273); rounds 2–13 closed** (see below).
|
*Status: **all deliverables landed; self-check all-green (14264/14264); rounds 2–14 closed** (see below).
|
||||||
Node QA gates (selfcheck · scaffold · consistency) GREEN. v3.0-beta DISTRICT interface published (2–4
|
v3.0 RELEASE round: 400-seed district invariant sweep landed + CITY_SPEC §v3 frozen at `v3.0`. Goldens
|
||||||
venues, week schedule, town-wide posters); gig golden `0x1f636349`. Strict 6/6 is F's end-of-round gate.*
|
unchanged (synthetic `0x3fa36874`, gig `0x1f636349`) — no golden moved, no rng draw added this round.*
|
||||||
*Date: 2026-07-15 · owner: PROCITY-A (Opus 4.8)*
|
*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
|
## 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
|
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).
|
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
|
> **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 —
|
> `?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 —
|
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
|
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
|
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
|
**`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
|
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.
|
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`.
|
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)
|
## 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
|
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
|
// 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.
|
// invariants. Validates the DISTRICT interface every downstream lane (C/D/B/F) hangs off this round.
|
||||||
function gigSuite(plan, label) {
|
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 venues = plan.shops.filter(s => s.venue);
|
||||||
const byId = new Map(venues.map(v => [v.id, v]));
|
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);
|
const d = Math.hypot(p.x - nx, p.z - nz);
|
||||||
if (d < best) { best = d; bestEdge = e; }
|
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)` : ''));
|
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)}`);
|
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 ──────────────────
|
// ── 4. every facade skin referenced by the registry exists on disk ──────────────────
|
||||||
section('assets on disk');
|
section('assets on disk');
|
||||||
for (const f of allFacadeSkins()) ok(existsSync(join(ASSETS, f)), `registry facade exists: ${f}`);
|
for (const f of allFacadeSkins()) ok(existsSync(join(ASSETS, f)), `registry facade exists: ${f}`);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user