The carried brief (slipped R7) lands complete — all four items, qa.sh --strict GREEN.
1. Selfcheck PARITY: extracted structuralSuite(plan,label) and run the FULL invariant suite
on every (source, town) — synthetic + both OSM towns get identical coverage (storeys,
one-shop-per-lot, facing, within/cross-block overlap, corridor coverage, determinism,
exactly-one-openLate). ALL GREEN 1727/1727 (was 1362; OSM went subset → full ×2 towns).
2. SECOND TOWN: Katoomba (Blue Mountains, 19 shops, op-shop/book heavy) — regional contrast
to inner-Melbourne. osm_fixture.js → OSM_TOWNS{melbourne,katoomba}+DEFAULT_TOWN;
generatePlanFor(seed, source, {town}); ?plansrc=osm&town=katoomba. Golden 0x0f652510.
Melbourne 0x34cfdec0 + synthetic 0x3fa36874 UNCHANGED (town key kept off the plan).
3. "Add a town" recipe in LANE_A_NOTES (R8): extract → OSM_TOWNS → pin golden → tell F.
4. Normalization + LOG: importer bends real data to the contracts (unknown kind→opshop,
hours-clamp so only one shop is openLate, video-preferred landmark w/ fallback) and logs
it via {report}; selfcheck prints per-town (e.g. melbourne hoursClamped:5, openLate:video).
For F: three goldens to pin, gate keyed (seed, plansrc, town); selector gained {town} — see
LANE_A_NOTES R8. Screenshots docs/shots/laneA/osm-{melbourne,katoomba}.png.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The carried-over OSM plan source (no-showed R4–R5) lands complete, invariants green — a
second CityPlan producer behind the same contract, so B–F consume it unchanged.
- osm_fixture.js: 95 REAL inner-Melbourne secondhand/charity/music/book/toy/antique shops,
extracted deterministically from thriftgod city_source.json (Overpass cache), imported as a
module ⇒ ZERO network.
- plan_osm.js: generatePlanOSM(seed) — project lat/lon→metres, bin shops into latitude-band
avenues off a spine (real row + real E–W order preserved), march into a valid non-overlapping
CityPlan, centre on origin, report true 362×486 bbox, real shop names, one openLate video.
- index.js: generatePlanFor(seed, source) selector — default 'synthetic' BYTE-IDENTICAL
(golden 0x3fa36874 untouched); 'osm' → the importer. generatePlanOSM exported for F.
- selfcheck.js: parameterized by source — OSM runs the full structural invariant suite
(frontEdge/facing/no-overlap/chunk-coverage/finite/JSON/determinism/openLate) + its own
golden 0x34cfdec0; synthetic-only brief checks skipped. ALL GREEN 1362/1362.
- map.html: ?plansrc=osm renders it (reference wiring for F). Screenshot in docs/shots/laneA/.
- CITY_SPEC: plan-source note; LANE_A_NOTES R6: seam + goldens + shape-diffs for F's gate (F2).
Honest cut: street geometry regularized (real row+order drive it, exact metres do not);
name-parody deferred. qa.sh --strict GREEN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1-tag round. Two confirmations + one hardening fix; qa.sh --strict GREEN (4/4).
A2 — openLate: F's night gate `hours[1] >= 22` matched 4–15 shops/seed (regular videos
jitter base-21 → 22), not the single landmark the shell assumes. Fixed in plan.js: regular
shops close by LATE_HOUR-1 (21:00); only the deterministically chosen openLate landmark
reaches ≥22, pick is now video-first. So `hours[1] >= 22` and the `openLate` field are the
same singleton, always the video rental (verified 15 seeds). Makes generation match
web/index.html:159-161 + soak.py:68 ("at night only the openLate shop is open"). selfcheck
asserts exactly-one / field⟺threshold / is-video. ALL GREEN 1301/1301.
A1 — Lane F's F2 permittedMax storeys scoping: confirmed 0 warnings across 7 seeds + a
false-negative test (injected video@2 in a scratch checker → caught every seed).
GOLDEN.hash 0x098eec2b → 0x3fa36874: intended A2 output change (a few videos close 21 not
22 for seed 20261990), NOT drift; F2 is tooling-only. Same lots/shops/types/names/skins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per ROUND3_INSTRUCTIONS 'Lane A — on-call + one verification':
- F finding #5 (shops with storeys outside registry range) verified STALE across 8
seeds: zero violations. Single-storey types (video/milkbar/stall) never exceed 1;
the only shops above registry max are the intentional 3-storey corner anchors
(tall-capable types), exactly per CITY_SPEC. Round-1 cornerBoost gate already
prevents the reported bug. This is also the source of qa.sh's 5 benign storeys
warnings (flagged for Lane F to scope to permittedMax).
- Hardened the selfcheck storeys assertion from the too-lax <=max(mx,3) to the exact
permitted range (max>=2 ? min(max+1,3) : max), so a single-storey type exceeding 1
now fails loudly. Proven non-vacuous. selfcheck.js only — plan output/fingerprint
unchanged (golden 0x098eec2b still matches, confirmed per task 2).
- Documented the storeys corner-anchor rule in the CITY_SPEC schema.
- hours/openLate contract unchanged; ready for Lane F's §3.5 consumer.
qa.sh --strict GREEN (4/4). Committed atomically by pathspec (shared-tree race).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
My round-2 Lane A changes were committed under Lane C's message (4235060) due to a
shared-index race. Content is safe & correct; this note flags it for review. Committed
atomically via pathspec to avoid re-triggering the race.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Answers Lane F §8 (the counter is kind:'counter' in `places`) and closes the
keeper-spawn gap: buildInterior now returns room.counter = { mesh, pose, stand }
and tags the counter interactable with userData.keeperStand. `stand` = {x,z,ry}
where a shopkeeper stands (service side, facing the customer), in room-local
space, rig-front = local -Z per keepers.js. Deterministic; purely additive.
Verified in-browser: 36-build type sweep + 50-room soak — stand in-bounds,
faces customer (dot>0 all), carried on `places` (deep-equal), deterministic;
no change to existing placement, path, leaks, or perf (avg 3.6ms / worst 7.7ms,
geo/tex leak 0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>