Lane A round 20: spine-poster cap scaled by shop count (v4.0-beta, ledger #1)
Closes my own R19 finding + F's one open budget edge. A sparse REAL town has few shops but a big edge graph, so the 'two posters per main edge' pole run reads busy (katoomba 438) and drove venue_night to 200,965 tris (+0.5% over the 200k soft ceiling). - gigs.js buildPosters caps the spine pole run at POSTER_PER_SHOP × shops (1.5/shop, floor 12) — a seeded subsample. SCALED BY SHOP COUNT, not edge count (the whole point). katoomba 438→31 posters (~2x the synthetic feel), fremantle 32, castlemaine 14. NO-OP on the shop-dense synthetic town (493 shops ⇒ cap ~740 ≫ its ~15 posters), so the synthetic gig golden 0xb1d48ea1 stays FROZEN. - Posters are gig-layer (withGigs), so the real-town BASE goldens are unchanged (correctly green). Added REAL_TOWN_GIG_GOLDENS (the full district+capped-poster plan per town: katoomba 0xbd332e83, …) to regression-guard the capped output byte-exact, not just via districtInvariants. classic/gig/marched fixtures frozen; selfcheck ALL GREEN 51675/51675; scaffold + consistency GREEN; cross-process deterministic. → F: re-measure venue_night, the +965 edge should close. Density absorb (ledger #2) pends E's widened caches — the poster cap already reads the live shop count, so it auto-scales to the widened towns. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
06fe64875a
commit
955ec3497b
@ -1,11 +1,30 @@
|
||||
# LANE A — CITYGEN · progress (PROCITY-A)
|
||||
|
||||
*Status: **all deliverables landed; self-check all-green (51670/51670); rounds 2–19 closed** (see below).
|
||||
v4.0-alpha alpha-close: fragmentation resolved (105→1 component on katoomba), E's spine-poster finding
|
||||
fixed, all 5 real towns pinned on real-roads goldens (katoomba `0xb7ffbca2`). classic/gig/marched-fixtures
|
||||
frozen.*
|
||||
*Status: **all deliverables landed; self-check all-green (51675/51675); rounds 2–19 closed, R20 poster-cap
|
||||
landed** (see below). v4.0-beta: spine-poster cap scaled by shop count (katoomba 438→31, closes F's
|
||||
venue_night edge); real-town gig goldens pinned. classic/gig/marched-fixtures frozen. Density absorb pends
|
||||
E's widened caches.*
|
||||
*Date: 2026-07-16 · owner: PROCITY-A (Opus 4.8)*
|
||||
|
||||
## Round 20 (2026-07-16) — v4.0-beta THE FIVE TOWNS: the poster cap (density absorb pends E)
|
||||
|
||||
Two Lane-A deliverables; the first (independent) landed this commit, the second waits on E's widened caches.
|
||||
|
||||
1. **Poster cap (ledger #1) — LANDED.** My R19 finding (real katoomba: 438 spine posters, driving F's one
|
||||
budget edge, `venue_night` 200,965 tris) is fixed. `gigs.js buildPosters` caps the spine pole run at
|
||||
**`POSTER_PER_SHOP × shops`** (1.5/shop, floor 12) — a seeded subsample of the run. **Scaled by shop
|
||||
count, not edge count** (the whole point: a sparse real town has few shops but a big graph). Result:
|
||||
katoomba **438 → 31** posters (~2× the synthetic feel), fremantle 32, castlemaine 14. **No-op on the
|
||||
synthetic town** (493 shops ⇒ cap ~740 ≫ its ~15 posters), so the synthetic gig golden `0xb1d48ea1` is
|
||||
frozen. Posters are gig-layer, so the real-town BASE goldens are unchanged — I added
|
||||
`REAL_TOWN_GIG_GOLDENS` (the full district+poster plan per town: katoomba `0xbd332e83`, …) to
|
||||
regression-guard the capped output byte-exact. selfcheck ALL GREEN **51675/51675**; scaffold + consistency
|
||||
GREEN; cross-process deterministic. → F: re-measure `venue_night`, the +965 edge should close.
|
||||
2. **Density absorb (ledger #2) — PENDING E.** E re-fetches general retail (C's class→archetype table) and
|
||||
re-emits the five caches at 3–6× shop count. When they land I load-test seating/overflow/venue placement
|
||||
and re-pin the real goldens once. The poster cap already reads the live shop count → auto-scales to the
|
||||
widened towns.
|
||||
|
||||
## Round 19 (2026-07-16) — v4.0-alpha CLOSE: fragmentation ruling + findings hardened + goldens pinned
|
||||
|
||||
A was the round's spine (the A→E→A finalization deferred from R18). Every filed finding resolved; the tag is
|
||||
|
||||
@ -311,7 +311,11 @@ camera are all `+Z`; position `lot + (sin ry, cos ry)·(d/2+ε)`), turned to rea
|
||||
R15 it was on the `−Z` back and invisible from the street). So the poster never sits over a back-street's
|
||||
bitumen, **`pickVenues` prefers a band_room/rsl lot whose facade clears every carriageway by ≥ `POSTER_CLEAR`**
|
||||
(ROUND16 ledger #6 — exported from the barrel; the selfcheck asserts the same floor). Plus a seeded run along
|
||||
the spine — 2 per main edge (or, when a town has no main spine, every edge), at `t ∈ [0.2,0.8]` of the block, on **either verge**. Different venues' posters share the same pole
|
||||
the spine — 2 per main edge (or, when a town has no main spine, every edge), at `t ∈ [0.2,0.8]` of the block, on **either verge**;
|
||||
a candidate that would overhang a **real** crossing carriageway is skipped (ROUND19, E's finding), and the
|
||||
run is **capped at `POSTER_PER_SHOP × shops`** (ROUND20) — a sparse real town has few shops but a big edge
|
||||
graph, so the raw "2 per edge" run reads busy (katoomba 438 → 31). The cap never binds on the shop-dense
|
||||
synthetic town, so its gig golden is frozen. Different venues' posters share the same pole
|
||||
runs — that IS the district reading. `ry` follows the house convention (a poster's printed face =
|
||||
`(−sin ry, −cos ry)`, so `atan2` of the *away-from-viewer* normal); B places E's poster skin at `(x, z)`
|
||||
rotated `ry`; F wires "poster → that venue's tonight-gig".
|
||||
|
||||
@ -2,6 +2,28 @@
|
||||
|
||||
Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`.
|
||||
|
||||
## Round 20 (2026-07-16) — v4.0-beta: the poster cap (density absorb pending E's widened caches)
|
||||
|
||||
### → F: **poster cap landed — re-measure `venue_night`** (your +965-tri edge should close)
|
||||
My R19 finding (438 posters on katoomba) is fixed. `gigs.js` now **caps the spine pole run at
|
||||
`POSTER_PER_SHOP × shops` (1.5/shop, floor 12)** — a seeded subsample. Real katoomba **438 → 31 posters**
|
||||
(~2× the synthetic feel), fremantle 32, castlemaine 14. Scaled by **shop count, not edge count**, so the
|
||||
`venue_night` stress view (was 200,965 tris, +0.5% over 200k) should drop well under. Re-measure and close it.
|
||||
|
||||
### Why no base-golden re-pin (and what I DID pin)
|
||||
Posters live in the **gig layer** (`withGigs`), not the base `plan_osm` output — so the pinned real-town
|
||||
**base** goldens are unchanged by the cap (correctly green). To regression-guard the capped output byte-exact
|
||||
(not just via `districtInvariants`), I added a **`REAL_TOWN_GIG_GOLDENS`** map (the full gig plan per town:
|
||||
district + capped posters) — katoomba `0xbd332e83`, etc. Synthetic gig golden `0xb1d48ea1` **frozen** — the
|
||||
cap never binds on the shop-dense synthetic town (493 shops ⇒ cap ~740 ≫ its ~15 posters).
|
||||
|
||||
### Density absorb — PENDING E's widened caches (ledger #2)
|
||||
E re-fetches general retail (per C's class→archetype table) and re-emits the five caches at 3–6× shop count.
|
||||
When they land (E→A "caches re-emitted"), I load-test seating capacity + overflow accounting (`norm.dropped`
|
||||
stays honest) + venue placement, then re-pin the real goldens **once** (base + gig). The R18 overflow fence
|
||||
is exactly the load-bearing part now — I'll widen edge capacity / split runs if a dense edge overwhelms. My
|
||||
poster cap already reads the LIVE shop count, so it auto-scales to the widened towns (no change needed).
|
||||
|
||||
## Round 19 (2026-07-16) — the alpha close: fragmentation resolved + findings hardened + goldens pinned
|
||||
|
||||
### → B / C / D / F: **`katoomba_real` boots on real roads, golden pinned, findings resolved** (the handshake)
|
||||
|
||||
@ -23,6 +23,10 @@ const r4 = v => Math.round(v * 10000) / 10000;
|
||||
// selfcheck asserts the same floor — import POSTER_CLEAR there, don't re-guess it.
|
||||
export const POSTER_CLEAR = 0.5;
|
||||
const FACADE_PROUD = 0.06;
|
||||
// ROUND20 poster cap: the spine pole run is "2 per main edge", which on a sparse REAL town (few shops, big
|
||||
// edge graph) reads busy (katoomba: 438). Cap it SCALED BY SHOP COUNT — a seeded subsample of the run. On
|
||||
// the synthetic town the shop count is in the hundreds, so the cap never binds and the gig golden is frozen.
|
||||
const POSTER_PER_SHOP = 1.5, POSTER_SPINE_FLOOR = 12;
|
||||
// nearest point on segment AB to P (same helper the selfcheck uses; kept local so gigs.js stays pure).
|
||||
const nearestSeg = (px, pz, ax, az, bx, bz) => {
|
||||
const dx = bx - ax, dz = bz - az, L2 = dx * dx + dz * dz || 1;
|
||||
@ -275,7 +279,7 @@ function buildPosters(plan, citySeed, venues, gigs) {
|
||||
};
|
||||
const pr = rng(citySeed, 'posters', 0);
|
||||
const PER_EDGE = 2;
|
||||
let k = 0;
|
||||
const spineCands = [];
|
||||
for (const e of runs) {
|
||||
const a = nodeById.get(e.a), b = nodeById.get(e.b);
|
||||
if (!a || !b) continue;
|
||||
@ -291,9 +295,15 @@ function buildPosters(plan, citySeed, venues, gigs) {
|
||||
const nx = side > 0 ? -uz : uz, nz = side > 0 ? ux : -ux;
|
||||
const x = a.x + ux * len * t + nx * off, z = a.z + uz * len * t + nz * off;
|
||||
if (onCarriageway(r2(x), r2(z))) continue; // check the r2-rounded position `add` stores — overhangs a crossing → skip (E's R18 finding)
|
||||
add(x, z, Math.atan2(nx, nz), bill[k++ % bill.length].gigId);
|
||||
spineCands.push({ x, z, ry: Math.atan2(nx, nz) });
|
||||
}
|
||||
}
|
||||
// ROUND20 poster cap: thin the pole run to ~POSTER_PER_SHOP × shops (a seeded subsample), so a sparse
|
||||
// real town reads like a main street, not a poster wall. cap ≫ candidates on synthetic ⇒ no-op ⇒ frozen.
|
||||
const cap = Math.max(POSTER_SPINE_FLOOR, Math.round(POSTER_PER_SHOP * plan.shops.length));
|
||||
const chosen = spineCands.length > cap ? shuffle(rng(citySeed, 'postercap', 0), spineCands.slice()).slice(0, cap) : spineCands;
|
||||
let k = 0;
|
||||
for (const c of chosen) add(c.x, c.z, c.ry, bill[k++ % bill.length].gigId);
|
||||
return posters;
|
||||
}
|
||||
|
||||
|
||||
@ -457,6 +457,13 @@ const REAL_TOWN_GOLDENS = {
|
||||
katoomba_real: 0xb7ffbca2, newtown_real: 0x6f984c81, fremantle_real: 0xfb197000,
|
||||
bendigo_real: 0x21179b3b, castlemaine_real: 0x7c47f639,
|
||||
};
|
||||
// ROUND20: the GIG-layer golden per real town (district + capped posters) — the base golden above is
|
||||
// gigs-off so it never captured the poster cap. Pinned here so the ROUND20 poster cap (and the density
|
||||
// absorb to come) are regression-guarded byte-exact, not just by districtInvariants.
|
||||
const REAL_TOWN_GIG_GOLDENS = {
|
||||
katoomba_real: 0xbd332e83, newtown_real: 0xd36f09ca, fremantle_real: 0xca42f569,
|
||||
bendigo_real: 0x93ae8765, castlemaine_real: 0x9a4e904c,
|
||||
};
|
||||
const townFiles = (existsSync(TOWNS_DIR) ? readdirSync(TOWNS_DIR) : []).filter(f => f.endsWith('.json'));
|
||||
if (!townFiles.length) console.log(" (none yet — the contract + hardening are live, ready for E's build_towns.py)");
|
||||
for (const f of townFiles) {
|
||||
@ -470,8 +477,12 @@ for (const f of townFiles) {
|
||||
for (const s of GIG_SEEDS) districtInvariants(generatePlanFor(s, 'osm', { gigs: true, town: key, cache }), `real/${key} ${s}`);
|
||||
const hash = xmur3(JSON.stringify(generatePlanOSM(20261990, key, { cache })))() >>> 0;
|
||||
const want = REAL_TOWN_GOLDENS[key];
|
||||
if (want === undefined) console.log(` ⚠ real/${key}: UNPINNED — add REAL_TOWN_GOLDENS['${key}'] = 0x${hash.toString(16).padStart(8, '0')}`);
|
||||
else ok(hash === (want >>> 0), `real/${key}: golden 0x${hash.toString(16)} matches pinned 0x${(want >>> 0).toString(16)}`);
|
||||
if (want === undefined) console.log(` ⚠ real/${key}: base UNPINNED — add REAL_TOWN_GOLDENS['${key}'] = 0x${hash.toString(16).padStart(8, '0')}`);
|
||||
else ok(hash === (want >>> 0), `real/${key}: base golden 0x${hash.toString(16)} matches pinned 0x${(want >>> 0).toString(16)}`);
|
||||
const ghash = xmur3(JSON.stringify(generatePlanFor(20261990, 'osm', { gigs: true, town: key, cache })))() >>> 0;
|
||||
const gwant = REAL_TOWN_GIG_GOLDENS[key];
|
||||
if (gwant === undefined) console.log(` ⚠ real/${key}: gig UNPINNED — add REAL_TOWN_GIG_GOLDENS['${key}'] = 0x${ghash.toString(16).padStart(8, '0')}`);
|
||||
else ok(ghash === (gwant >>> 0), `real/${key}: gig golden 0x${ghash.toString(16)} matches pinned 0x${(gwant >>> 0).toString(16)}`);
|
||||
}
|
||||
|
||||
// ── 4. every facade skin referenced by the registry exists on disk ──────────────────
|
||||
|
||||
Loading…
Reference in New Issue
Block a user