diff --git a/docs/A-progress.md b/docs/A-progress.md index 6d24dd6..a97a262 100644 --- a/docs/A-progress.md +++ b/docs/A-progress.md @@ -1,11 +1,38 @@ # LANE A — CITYGEN · progress (PROCITY-A) -*Status: **all deliverables landed; self-check all-green (66419/66419); rounds 2–21 closed** (see below). -R21: venue cluster-adjacency bias — D's relocation finding resolved (venues under 3 shops/160 m: 11 of 15 -→ 0); gig goldens re-pinned; synthetic frozen by construction. R20: poster cap (katoomba 438→31) + E's -density widening absorbed (5 towns at 3–6× shops). classic/gig/marched-fixtures frozen.* +*Status: **all deliverables landed; self-check all-green (161,300/161,300); rounds 2–22 closed** (see below). +R22 (v4.0 epoch close): the town pack absorbed — **23 towns, 1,210 shops, 36 goldens pinned in one commit**; +every town 1 connected component + 3 venues; drops 6.4%, counted by cause. classic `0x3fa36874` / +gig `0xb1d48ea1` / marched fixtures frozen.* *Date: 2026-07-16 · owner: PROCITY-A (Opus 4.8)* +## Round 22 (2026-07-16) — v4.0 THE EPOCH CLOSE: the pack absorbed + 36 goldens pinned (ledger #2) + +E's 17 new towns + Lane G's `newtown_godverse` fed through the lift and pinned in ONE golden commit +(`REAL_TOWN_GOLDENS` + `REAL_TOWN_GIG_GOLDENS`: 5 → **23 towns each**, zero unpinned). + +**The headline: everything built this epoch held at 23-town scale, first try.** 1,210 shops → 1,133 seated +(77 dropped, **6.4%**, counted by cause); **1 connected component and 3 venues on every one of the 23 towns**. +`fitzroy` — 160 shops, 2× anything absorbed before — landed 139 seated, 1 component, 3 venues, 212 posters, +all invariants green. The junction-protected DP, fragmentation ruling, cluster bias, poster cap and capacity +fence needed **no new hardening**: the pack needed absorbing, not fixing. + +- **Graded cluster fallback (the one refinement).** Three towns have no candidate at the floor at all + (`ballarat`/`toowoomba`/`newtown_godverse` top out at 2 shops within 160 m); R21's bias switched off there, + stranding venues at 0. It now falls back to the **densest available** — ballarat 0,2,0 → **2,2,2**, godverse + 0,1,1 → **2,2,2**, toowoomba 1,0,0 → 1,2,1. Fires only where the floor is unreachable ⇒ **no already-pinned + town moved** (verified). Acceptance at pack scale: **20 of 23 towns have every venue ≥3**; the three above + sit at their town's genuine ceiling — the carved-out case, stated explicitly. +- **Drop outliers, counted not hidden:** `daylesford` 19% (32 shops on a 171-road graph — the thinnest in the + pack, all overflow), `fitzroy` 13%. `RGAP` is already 0.5 m and `NODE_CLEAR` 3 m; the only lever left is + spilling onto a different street, which buys a few percent by putting a shop on a road it isn't on. Same + ruling as R20: **drop and count beats teleport**. +- selfcheck ALL GREEN **161,300/161,300**; scaffold + consistency GREEN; cross-process deterministic + (pack-xor `0xca140954`). classic / gig / marched fixtures frozen. + +Handshake fired → C + D + F: the pack is in, 23 towns pinned, start your verifies. For the tri diet +(ledger #1): if B's layer-by-layer names plan geometry as the `venue_night` driver, I own the fix. + ## Round 21 (2026-07-16) — v4.0-beta: the venue cluster-adjacency bias (ledger #1, the round's spine) D filed it: katoomba's pub sat 1,000 m from the retail cluster — a gig with no passing crowd. Measuring all diff --git a/docs/LANES/LANE_A_NOTES.md b/docs/LANES/LANE_A_NOTES.md index 675593d..60082da 100644 --- a/docs/LANES/LANE_A_NOTES.md +++ b/docs/LANES/LANE_A_NOTES.md @@ -2,6 +2,51 @@ Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`. +## Round 22 (2026-07-16) — v4.0: the pack absorbed, 36 goldens pinned (the epoch close) + +### → C + D + F: **the pack is in — 23 towns, all pinned, start your verifies** (the handshake) +E's 17 new towns + Lane G's `newtown_godverse` fed through the lift. **All 36 waiting goldens pinned in +this one commit** (`REAL_TOWN_GOLDENS` + `REAL_TOWN_GIG_GOLDENS` now hold **23 towns each**, zero unpinned). +selfcheck **ALL GREEN 161,300/161,300**; scaffold + consistency GREEN; cross-process deterministic (pack-xor +`0xca140954`). classic `0x3fa36874` / gig `0xb1d48ea1` / marched fixtures **frozen**. + +### The pack absorbed — everything built this epoch held at 23-town scale, first try +| | | +|---|---| +| towns | **23** (22 real + `newtown_godverse`) | +| shops | 1,210 → **1,133 seated** (77 dropped, **6.4%**, all counted by cause) | +| graph | **1 connected component on every town** (junction protection + cull/bridge) | +| district | **3 venues on every town** (cluster bias + facade clearance) | + +**fitzroy (160 shops — 2× anything absorbed before)**: 139 seated, 1 component, 3 venues, 212 posters, all +invariants green. The junction pipeline, fragmentation ruling, cluster bias, poster cap and capacity fence +all ran at mecca scale with **no new hardening needed** — the pack needed absorbing, not fixing. + +### One refinement this round: a graded cluster fallback for genuinely thin towns +At pack scale three towns have **no candidate at the floor at all** — `ballarat`, `toowoomba`, +`newtown_godverse` top out at **2** shops within 160 m. R21's bias switched off entirely there, stranding +venues at 0. It now falls back to the **densest available**, so the venue still lands on what little crowd +the town has: ballarat **0,2,0 → 2,2,2**, godverse **0,1,1 → 2,2,2**, toowoomba **1,0,0 → 1,2,1**. +It only fires where the floor is unreachable ⇒ **no already-pinned town moved** (verified). + +**Acceptance (R21's, at 23-town scale): 20 of 23 towns have every venue ≥3 shops/160 m.** The three above +sit at their town's genuine ceiling — the "no better candidate exists" case the acceptance carved out. +**Saying so explicitly, as asked.** + +### Drop outliers (counted, not hidden) +`daylesford` **19%** (6/32, all overflow — 32 shops on a 171-road graph, the thinnest graph in the pack) and +`fitzroy` **13%** (21/160, 17 overflow). Both are the R20 capacity fence doing its job: `RGAP` is already +0.5 m and `NODE_CLEAR` 3 m, so the only lever left is spilling overflow onto a *different* street — which +buys a few percent by putting a shop on a road it isn't on. Same call as R20: **drop and count beats +teleport**. Everything ≤6.4% pack-wide. + +### → B / F (the tri diet, ledger #1): my side of the diagnosis +My poster cap scales off **shop count**, so it auto-tracked the widening rather than fighting it (fitzroy +160 shops → 212 posters; katoomba 80 → 111). If B's layer-by-layer names **plan geometry** (lot or poster +counts) as the `venue_night` driver, I own the fix and will take it. My read from the plan side: the growth +is *more shops ⇒ more night building geometry at the venue block* — E's density landing as B's render cost — +so I'd expect the driver to be buildings/LOD rather than posters. B's numbers decide it, not my expectation. + ## Round 21 (2026-07-16) — the venue cluster bias (D's relocation finding) + goldens re-pinned ### → C + D: **cluster bias landed, goldens re-pinned — start your verifies** (the handshake) diff --git a/web/js/citygen/gigs.js b/web/js/citygen/gigs.js index 782023c..055ef78 100644 --- a/web/js/citygen/gigs.js +++ b/web/js/citygen/gigs.js @@ -130,6 +130,15 @@ function pickVenues(plan, citySeed) { if (realData) { const clustered = pool.filter(s => clusterCount(s) >= CLUSTER_MIN); if (clustered.length) field = clustered; + else { + // ROUND22 (pack scale): a genuinely thin town can have NO candidate at the floor — ballarat, + // toowoomba and newtown_godverse top out at 2 shops within 160 m. Rather than drop the bias + // entirely (which stranded venues at 0), fall back to the DENSEST available: the venue still + // lands on what little crowd the town has. Only fires where the floor is unreachable, so no + // already-pinned town moves. + let best = 0; for (const s of pool) { const n = clusterCount(s); if (n > best) best = n; } + if (best > 0) field = pool.filter(s => clusterCount(s) >= best); + } } let cand; diff --git a/web/js/citygen/selfcheck.js b/web/js/citygen/selfcheck.js index 278b209..9054f48 100644 --- a/web/js/citygen/selfcheck.js +++ b/web/js/citygen/selfcheck.js @@ -455,8 +455,29 @@ const TOWNS_DIR = join(HERE, '..', '..', 'assets', 'towns'); // the fragmentation ruling (junction-protected DP + cull/bridge) + E's spine-poster fix. Amendment law: // the alpha changed nothing outside the cache-schema path; synthetic/fixtures/classic/default stay frozen. const REAL_TOWN_GOLDENS = { - katoomba_real: 0x420f677d, newtown_real: 0x741c18ec, fremantle_real: 0x73d385df, - bendigo_real: 0xc175194e, castlemaine_real: 0x26c128c9, + adelaide_real: 0x5c790920, + ballarat_real: 0xf0a9e8a1, + bendigo_real: 0xc175194e, + bowral_real: 0xb23ee067, + braddon_real: 0x2f388775, + brunswick_real: 0x7ecb5c3e, + castlemaine_real: 0x26c128c9, + darwin_real: 0xcfb1917f, + daylesford_real: 0x4ec7352c, + fitzroy_real: 0x6549f61b, + fremantle_real: 0x73d385df, + geelong_real: 0x2f65591e, + glebe_real: 0x908e1835, + hobart_real: 0xa96328b9, + katoomba_real: 0x420f677d, + launceston_real: 0x32b1ab7b, + marrickville_real: 0x60148e37, + newcastle_real: 0xbb2c3050, + newtown_godverse: 0x24e63feb, + newtown_real: 0x741c18ec, + northbridge_real: 0x1a645848, + toowoomba_real: 0x8b635722, + westend_real: 0x5b1e5ffe, }; // 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 @@ -464,8 +485,29 @@ const REAL_TOWN_GOLDENS = { // ROUND21: re-pinned for the venue cluster-adjacency bias (D's relocation finding). Only the GIG goldens // move — venue selection is gig-layer, so the base town goldens above are untouched. const REAL_TOWN_GIG_GOLDENS = { - katoomba_real: 0xbf586b77, newtown_real: 0xacdb3eee, fremantle_real: 0xcf3426fc, - bendigo_real: 0x646b2c23, castlemaine_real: 0xf9713a69, + adelaide_real: 0x24ad3c27, + ballarat_real: 0xf097c331, + bendigo_real: 0x646b2c23, + bowral_real: 0xac809f0b, + braddon_real: 0xc3e5cfc0, + brunswick_real: 0x8c08914e, + castlemaine_real: 0xf9713a69, + darwin_real: 0xbc5605b5, + daylesford_real: 0x2fb34c8a, + fitzroy_real: 0x690e5ef9, + fremantle_real: 0xcf3426fc, + geelong_real: 0x6f47ec2f, + glebe_real: 0x042f01fb, + hobart_real: 0x22f80d7b, + katoomba_real: 0xbf586b77, + launceston_real: 0xeaf7ff4d, + marrickville_real: 0xea21be51, + newcastle_real: 0x66fc2bf9, + newtown_godverse: 0xc5ed0b92, + newtown_real: 0xacdb3eee, + northbridge_real: 0x83a1d5be, + toowoomba_real: 0x9d34ccfe, + westend_real: 0xb2c7ddb6, }; // `index.json` is E's towns INDEX (key/town/state/shops/roads for B's selector), not a town cache — skip it. const townFiles = (existsSync(TOWNS_DIR) ? readdirSync(TOWNS_DIR) : []).filter(f => f.endsWith('.json') && f !== 'index.json');