The spike reaches the game. E's look.glb (mesh-free, 65 joints, 6.5s 'Look') is bound
through the fleet gate; street peds now glance around during window-shop stops. `lean`
is absent and never existed (E's R28 measurement). Goldens untouched — clips sit outside
every plan hash; the synthetic anchor stays R27's amended 0x5f76e76.
WIRED: loadPedFleet(base, {sit, look}) -> fleet.lookClip, the exact twin of the R16 sit
gate; the shell passes look:!CLASSIC so ?classic fetches NOTHING (a raw fetch would have
breached the zero-fetch-delta covenant — E flagged it, the gate is the answer).
makeActor gains lookA, played only on setLooking(true) — crossfaded (0.3s), not snapped
like sit, since idle->idle-variant reads naturally and there's no replant for a fade to
fight. sim.js gains a dedicated `glance` rng stream, rolled ALWAYS (deterministic) but
only flipped when a clip exists => classic inert at the source. Mutually exclusive with
bench-sit (sit rolls first at 35%, glance takes 40% of the rest). RigPool threads
lookClip; release snaps the glance off so no rig is pooled mid-look.
THE FINDING — my own prediction was wrong, and the measurement caught it. I wrote that a
STANDING clip needs no foot replant ("no hip descent to recover — that trap was sit's
alone"). Measured over the full loop: idle keeps the lowest bone within 4cm of the ground
(-0.04..0.00); look.glb ranges -0.009..+0.205 — the ped HOVERS up to 20.5cm, drifting
through the loop. Same trap, different coat: _rotOnly drops the Hips POSITION track for
rig-height independence, so the clip's authored weight-shift dip can't move the hips and
comes out as the FEET RISING. Sit's one-time replant can't fix it — the float varies per
frame.
FIX: plantFeet() — the per-frame twin of sit's replant. Reset to bind plant -> measure
posed feet -> drop inner by float/scale. ABSOLUTE (reset->measure->correct), never
incremental, so it cannot drift. Reproduces the authored motion exactly: the hips dip
relative to the ground instead of the feet leaving it. Scale-aware like sit's. Foot bones
cached once; cost paid only by glancing peds, on frames their mixer ran (both mixer sites
— the drummer's post-mix lean taught that coupling rule).
PROOFS: foot float 0.205m -> 0.000m (pinned); head yaw sweep span 1.735 (-0.85..+0.89) so
the glance still animates; ?classic — no look fetch, no Look action on any rig, glance
never flips (0); identity signature unchanged, no glance term; sit AND glance never
simultaneous (0); 0 NaN; goldens 0x5f76e76 untouched. A glancing ped is NOT tagged
seated/posed — standing pose at standing stature, so F's no-giants gate needs no arm.
D->F: the pose gate HAS ITS SUBJECT — un-SKIP it. Fresh ?localdepot=1 boot shows a ped
glancing on the footpath, head sweeping, feet planted.
Spike 2 readiness: plantFeet() is the general machinery the brief asked for ("data, not
new code") — riffle and wall-lean should land as clip + roll; the lean's wall-contact
placement is the only genuinely new part.
FLAGGED (A's file, A rests): selfcheck.js:301 still says "the synthetic golden above stays
0x3fa36874" while line 267 pins the amended 0x5f76e76. A stale comment, not a rule — but
the same species as the epoch's five holds, so: flagged, not touched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
E: STOP — do not build the _lo variants. That work is unnecessary; the item is dead.
The oldest tri item on the books, killed under the wind-sway rule attached to the brief
("if the measured win is <20k tris in practice, KILL it with the measurement").
MEASURED, from the shipped depot GLBs: each of the five instruments is exactly 14,000
tris; band + amp on stage = 70,000. On paper a 3-4k _lo each "saves" ~52k, clearing the
20k bar easily.
But the rule says IN PRACTICE, and the brief named the reason — "interiors are
close-quarters". So I measured the rooms an LOD would have to fire in (live, all three
venue archetypes, interiorMode driven by hand since rAF is throttled):
pub 9.7 x 9.7m, diagonal 13.7m, 12522 tris, 38 draws
band_room 8.9 x 9.6m, diagonal 13.0m, 11699 tris, 35 draws
rsl 12 x 9.4m, diagonal 15.2m, 15115 tris, 38 draws (the biggest)
VERDICT: practical win = 0 tris. A player can NEVER be more than 15.2m from the band —
corner-to-corner in the largest venue, and the stage sits at one end so the real max is
shorter. An LOD is a distance trade; inside a <=15.2m room the band is never small — it
is the subject of the gig and of F's tour money shot. Any sane threshold (15-25m) never
fires; an aggressive 8m one fires in half a room at a distance where a 3-4k _lo is
visibly worse with no perceptual cover. The win isn't "small", it's STRUCTURALLY ZERO —
the distance an LOD needs does not exist in this game. Off the backlog forever.
No budget motivates it either: a venue interior is ~12-15k tris, ~85k with the band —
inside the 200k stress ceiling E's R22 tri diet restored, at 35-38 draws vs <=350.
SIDE-OBSERVATION for E (not a proposal, no breach motivates it): all five instruments
are EXACTLY 14,000 tris — a decimator target, not a measurement of the objects. A mic
stand does not need 14k. If instrument tris ever do drive a real breach, the lever is a
flat re-decimation by object complexity (mic stand -> ~1k), which beats any LOD because
it needs no distance. Filed so the next tri hunt starts from it.
Spike 1 (ledger #1) pending E's lean.glb — look.glb has landed, lean.glb not yet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
My carried v4.x finding, fixed post-release as scheduled. Runtime-only: no plan, no
golden, no flag, no fetch. selfcheck ALL GREEN 161300/161300, 0x3fa36874 frozen.
THE FINDING GREW WHILE IT WAITED: filed at 8.7% (measured at katoomba's isolated pub);
A's R21 cluster bias made it WORSE — venues now sit inside the retail cluster where peds
converge from every side, so 42.6% of in-gig-range peds (5094/11971) were chunk-blind.
THE OVERRIDE (measurement wins): the brief asked for "extend to chunk + 8 neighbours"
AND "byte-identical flags-off". I built the literal version and measured that those
cannot both hold: on the synthetic town (what ?classic=1 boots, gig layer absent) a full
3x3 scan sourced 127 of 855 finds (14.9%) from neighbour chunks — finds v2 never made.
The frozen v2 crowd would shop ~15% more. Covenant breach, in a clause of the same brief.
So the fix is scoped to the GIG path — which is what I actually filed:
- ordinary shops: own chunk only (v2 semantics, frozen — noticed from its own block)
- gig venues: the full 34m radius, honest across chunk edges ("follow the sound" is the
whole reason GIG_RANGE 34 > PATRON_RANGE 18)
- no gig on -> _gigVenues empty -> neighbour loop skipped entirely -> ?classic and every
flags-off boot take the IDENTICAL code path and cost to v2. Byte-identical BY
CONSTRUCTION, not by measurement luck.
Both ranges < CHUNK(64) so 3x3 is exact, not approximate; fixed dz/dx order keeps ties
deterministic as the old single-list scan was.
BEFORE -> AFTER: cross-chunk in-range peds seeing the gig 0% (structural: v2 scanned only
the ped's own chunk) -> 100% (katoomba 4/4, fitzroy 11/11). Same-chunk unchanged 100%.
Classic neighbour-sourced finds: 0 (127 under the rejected full scan). Determinism
identical. 0 NaN on katoomba/fitzroy/synthetic. Sim 0.056 -> 0.057 ms/frame (+2%, i.e.
0.006% of a 16.7ms budget); lookup 0.58us unchanged with no gig, 1.3-2.7us with a gig on
(fires per PATRON_STRIDE, not per frame). Fitzroy clean at 376 peds.
CARRIED, documented: PATRON_RANGE keeps v2's own chunk-blindness — fixing it costs the
classic covenant, and the covenant wins. A frozen v2 quirk now, not an oversight.
Known minor: a scheduled-but-closed gig still runs the loop for nothing (0.58->1.3us).
D->F: no blocker for v5.0-alpha; classic untouched by construction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Against A's pack absorb (3face97, 23 towns / 36 goldens). selfcheck ALL GREEN
161300/161300, synthetic 0x3fa36874 frozen.
FITZROY STRESS: PASS — the sim doesn't notice 2x scale. 160 shops -> 139 seated (21
dropped, counted), 1859 nodes / 2408 edges (biggest of the epoch), 376 active peds,
0 NaN, 0.08 ms/frame, 22 chunk rosters, 454 identity sigs, 103 patronage visits.
A's R21 cluster bias survives the mecca (min 8 shops within 160m). Nothing needed
touching; the citizen field is not a budget risk at any scale this epoch reaches.
THIN TAIL — "does 12 shops still read alive?" ANSWER: only if they CLUSTER. I checked
both 12-shop towns because they disagree, and the disagreement is the finding:
- darwin_real ALIVE: 27m median nearest-neighbour spacing, 7 of 12 shops within 160m of
the hub, 18 visits (13 hero).
- toowoomba_real DEAD: 388m median spacing (14x) across a 4.6km road network, only 3
shops within 160m of its own densest point, 1417 patronage checks -> 0 finds, 0 visits.
Not a shopping town — a road network with occasional shops.
Verified real, not an R20-style sampling error: 9 shops open at 14:00 in BOTH, camera
stood on the town's own densest point. 0 NaN / 0.05 ms/frame either way.
FILED for E/Fable (curation, not code): MIN_TOWN_SHOPS counts shops, but what makes a
town read alive is clustering. A cheap test that separates them: ">=5 shops within 160m
of the densest point" — darwin 7 PASS, toowoomba 3 FAIL. Toowoomba ships in the v4.0
pack today with no main street to walk. Cull or accept as a known thin-tail (it costs
nothing). E/Fable's call — A's venue bias correctly exempts it ("no better candidate").
D->F: citizens at pack scale GREEN, no D-side blocker for v4.0. Toowoomba is curation,
not a citizen defect — the tag isn't gated on it from me.
Carried to v4.x: GIG_RANGE 34m radius vs single-chunk lookup (8.7% chunk-blind).
Last flag before the retro: R20's main-street relocation half remains unowned (fitzroy
13 hero vs 90 texture visits); one call of this harness re-runs it if v4.x wants it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified A's venue cluster-adjacency bias (110c828), assigned from my R20 relocation
finding. selfcheck ALL GREEN 154141/154141 against A's re-pinned goldens.
ACCEPTANCE PASSES ON ALL FIVE (no venue with <3 shops in its 160m radius):
katoomba min 9 | newtown min 5 | bendigo min 5 | castlemaine min 4 | fremantle min 3.
Castlemaine's floor did NOT need an exemption.
MY HEADLINE FINDING IS CLOSED: katoomba's pub — "a gig with no passing crowd", 1 shop
at 1007m — now sits at 466m with 9 shops. Newtown is the dramatic fix: all three venues
were 2.2-2.4km out with 0-1 shops; its rsl now sits 29m from the hub with 11.
THE CROWD FOLLOWED (katoomba pub, same density/seed/21:00): passing crowd 34->66 (2x),
patron checks 226->442 (2x), inside frames 1042->1424, roster 2->3, 0 NaN.
TWO READINGS THAT NEED CARE:
- fremantle is the weakest pass: pub/rsl clear the >=3 bar but moved FURTHER from the
primary hub (969->1352m, 561->1292m) — A's bias optimises local cluster adjacency and
they found a secondary cluster. Letter satisfied, crowd healthy (pub roster 6, inside
2741), so not a miss — just named. A's call if they want it tighter.
- venue-win % DROPPING is not a regression (newtown rsl 100%->21%). It was only 100%
because the venue was alone; post-bias it has neighbours competing for finds while
total crowd rose. Isolation flatters that metric — ignore it post-bias.
D->F: relocation verify GREEN, no gate concern for v4.0-beta.
FYI F: selfcheck scans every towns/*.json on disk incl. E's UNTRACKED pack WIP — I saw
it transiently RED (newcastle_real, no open-late shop) then green minutes later. If you
gate while E is mid-fetch you may catch a false red; check if a failure is a pack town.
Still filed for v4.x: GIG_RANGE 34m radius vs single-chunk lookup (8.7% chunk-blind).
Flagged as unowned: R20's main-street relocation half (hero strip vs densest cluster).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-ran everything against A's absorbed graph (5d29863, selfcheck 51866/51866).
Katoomba barely moved from my pre-absorb read (null 77.2->78.1, visits 64->62,
hero share 30->29%), so the earlier conclusion held.
SIM VERDICT: zero changes needed at 4x density. All five towns: 0 NaN, chunk rosters
stream (15-24), identity sigs accumulate (262-483), patronage/loiter/bench-sit all fire,
up to 401 active peds, no degradation. Determinism unchanged (identity is graph+seed
keyed; shops don't touch it). D->F: GREEN.
THE FINDING (for E/C/Fable): the widening MOVED the main street off the secondhand strip.
Hero visit-share falling (29%->0%) is NOT a behaviour bug — patronage is type-agnostic, so
visits track the local mix exactly. The mix is the story. Pre-widening the densest cluster
WAS the secondhand strip (katoomba: 9 of 9 main-street shops were heroes). Post-widening
the centre of gravity relocated 308m (katoomba) / 434m (castlemaine) onto the general-
retail cluster, where heroes are a 0-19% minority. Castlemaine's main street now has ZERO
secondhand shops (0 of 4 heroes within 160m; median hero 463m away). The heroes weren't
drowned in place — they were demoted: the liveliest street is no longer theirs.
The good half stands: heroes kept absolute footfall (katoomba 17->18 visits) and the
street is 3.8x busier. The widening works; it overshot the dial. This is exactly the case
John's directive pre-authorised ("E may seed-subsample the texture classes"). My read
(measurement, not a ruling — E/C/Fable own it): subsample texture until the secondhand
strip is again the densest cluster. Sparse towns need it most (bendigo 7%, castlemaine 0%).
ALSO FILED (A/F): venues land off the retail hub at density — newtown's three are all
2190-2433m out with 0-1 neighbours; katoomba's pub still 1007m/1 (A's "pub facade bias"
fixed frontage, not position). Quality call, not a break; "venue placement still lands"
is A's ledger + F's district gate. Castlemaine shows the good case (band_room on the hub).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SELF-CORRECTION first. My part-1 note called the 95-98% patronage null rate
"sparsity-throttled" and made it the headline metric. Both halves were wrong:
(1) it was measured at 21:00, when only 4 of 69 shops are open — retail is shut BY
DESIGN, so a high night null is correct behaviour, not sparsity; (2) it sampled each
town's first venue, and katoomba's is the pub — 1000m from the retail cluster with 1
neighbour. I had parked the camera on the one isolated venue in town. Same town, same
seed: at the pub 99.4% null / 0 visits; on the actual main street 77.2% / 64 visits.
The gig-crowd table stands (it measures the mechanism — 0 NaN, rosters, venue-win —
which is location-independent); only my sparsity reading was bad.
THE REAL A/B (main street, 14:00, same seed, pre-cache from git vs E's widened):
hub density 8 -> 25 shops within 160m; null 90.2% -> 77.2%; total visits 17 -> 64
(3.8x); hero visits 17 -> 19; texture visits 0 -> 45; hero share 100% -> 30% of visits
from 26% of shops.
THE DIRECTIVE LANDS, measured from the crowd side: the secondhand heroes did NOT lose
footfall (17 -> 19). The 45 texture visits are added ON TOP, not taken from them. The
street is 3.8x busier while the heroes stay over-indexed. "More alive, not a different
game."
FILED: (a) venue placement at density — katoomba's rsl/band_room sit in the strip
(13/10 neighbours) but the pub is 1000m out with 1; a gig there has no passing retail
crowd. Quality observation, A/F's lane. (b) GIG_RANGE is a 34m radius but
_nearestOpenShop searches only the ped's own 64m chunk — 8.7% of in-range peds are
chunk-blind. Real but small; v4.x candidate, not a mid-beta hot-path change.
POST numbers are vs E's committed caches but BEFORE A's absorb (11/80 still overflow-
drop). Final re-run + the other four towns once A lands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unblocked half of "citizens at density": the gig crowd's BEHAVIOUR on all five
real towns' venues, measured BEFORE E's widening so the after-table is a real A/B.
Density re-audit follows once E's caches + A's absorb land.
BEHAVIOUR IS CORRECT ON ALL FIVE: 0 NaN everywhere; tonightRoster populates (2-7),
so R14 identity continuity holds on real bones; every town generates 3 venues
(pub/band_room/rsl) on night hours; and the surge works — whenever a patronage check
finds any shop, the gig venue beats ordinary shopfronts (venue-win 100% on newtown/
fremantle/bendigo, 74% katoomba). Castlemaine's 40% is the 6-shop floor, not a leak:
3 of its 6 shops ARE venues, so "other" hits are mostly other venues.
FINDING (quantifies Fable's ledger #2 "real towns are sparse"): the crowd is
SPARSITY-throttled — 94.9-97.7% of patronage checks return null on every town, because
peds spend nearly all their time in shopless chunks (katoomba: 19 shops in ~5 door-chunks
of a ~78x78-chunk town). The mechanism is fine; the town has no shops per chunk. The
density widening attacks exactly this — null% is my headline before/after metric.
FILED (not fixed): GIG_RANGE is a 34m radius but _nearestOpenShop searches only the
ped's own 64m chunk, so in-range peds across a chunk edge are blind to the gig — 8.7%
of in-range ped-samples (852/9740) on katoomba. Small vs the 97% sparsity throttle;
not worth a hot-path change mid-beta. Revisit if the widening leaves null% high.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Post-fix re-measure of my R18 finding against A's committed connectivity pass
(6fc4b40). Same harness, same seed.
VERDICT: fragmentation fully resolved, sim untouched. Real Katoomba:
components 105 -> 1, street-metres in main 58.2% -> 100%, shops stranded 2 -> 0
(1 bridged, 1 dropped past SEAT_MAX AND counted in norm.dropped), % near-crowd on
fragments ~10% -> 0%. selfcheck 51670/51670 green, katoomba golden pinned.
Mechanism check: re-ran R18's dead-straight-through-junction geometry — R18 dropped
the junction (deg 2, disconnected); R19 protects it (deg-4 crossroads, 1 component).
DP-collinear drop fixed at the source.
Sim confirm on the fixed bridged graph: 0 NaN over 1,303,200 samples, footpath-perfect,
loiter/bench-sit fire, patronage rose 24->41 (peds reach shops across the connected
graph), 0 fragment-stranded peds. Zero sim changes, as R18 predicted.
D->F: fragmentation verdict post-fix is GREEN — tag away. FYI B/F: edges rose 799->966
(junction protection + bridges), flagged for the tris budget.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audited the citizen sim on real irregular street topology. E landed katoomba_real's
real roads (484 ways -> 872 nodes/799 edges) mid-round, so this is measured on the
real Katoomba graph plus a crafted irregular proxy.
VERDICT: the CityPlan graph contract IS the contract — zero sim changes. On real
Katoomba (7.2deg junctions, 1303m edge, 105 components): 0 NaN over 1,095,500 samples,
peds footpath-perfect (0 off-footpath), loiter/bench-sit/patronage all fire, chunk
streaming works over non-grid, determinism byte-identical (1167 identity sigs, R14
chunk-local law holds). No source touched.
FILED FOR A: katoomba_real's graph is 105 components — main holds 58% of street-metres
+ 18/20 shops, but 42% of street-metres + 2 shops strand (~10% of near-main crowd
presence lands on tiny fragments). Primary cause is expected: median fragment 71.5m
from main = peripheral/bbox-clipped roads. Secondary (corroborated with Lane B on A's
selfcheck fixture): per-way Douglas-Peucker drops collinear junction points + no
mid-edge split — bites only the 9/85 fragments <10m from main. Reconciles with B:
B verified 126 intersections form (main component); I add the global measurement
(still 105 components). A fidelity knob (charter risk #4), not a blocker; sim degrades
gracefully (no crash). A owns the connect-tolerance decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Street peds occasionally sit during a window-shop loiter (verandah/footpath
edge). makeActor gains an opt-in setSitting with a SCALE-AWARE re-plant (the
R16 spawnRig re-plant was scale-naive; the sim scales these rigs per-citizen),
played only on sit so walkers/placeholders/?classic are byte-identical. Seeded
via a dedicated benchsit rng stream (no shift to turn/loit/patron; identity
signature unchanged). Classic inert at the source (roll gated on sitClip).
In-shell (default, seed 20261990): head bone 1.466->1.070, bone span
1.667->1.299 (in seated gate [0.9,1.5]), feet re-plant to y=0 and hold through
sit-idle, stand-up restores inner.y 0.929 byte-for-byte, procitySeated tagged,
innerRotY:pi throughout. 9 distinct peds sat naturally per soak. Classic:
fleetSitClip:false, everSat:0. Plan golden 0x3fa36874 unchanged. Draws 35/tris 6k.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The R16 seated drummer sat bolt-upright (the sit clip can't carry a pelvic tilt
— Hips.quaternion is unusable on these rigs). Fix: a small constant forward torso
lean applied POST-MIX. spawnRig({seated}) caches the mid-spine bone (seatBone);
seatedLean(spine) rotates it forward ~0.34 rad each frame after the mixer resets
it (constant offset, not accumulation); band.js calls it for m.seated members.
The drummer leans into the kit — head 0.28m forward of the hips, reads as a real
drummer from the side.
Byte-identical (my signature): standing rigs get no seatBone (found only in the
seated block) and seatedLean runs for seated figs only. Verified with the lean
live — walkers 188/188 face travel, keeper standing rig (stature 1.718,
hasSeatBone:false, innerRotY pi). No existing consumer moves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #2 — drummer sits. E's sit.glb is wired into the shared rig stack:
- FINDING: Fable's prescribed _rotWithHips (keep Hips.quaternion) MANGLES the
pose in-shell (head folds to hip height / laid flat) — the fleet rigs' bind
Hips orientation is incompatible with the Mixamo clip's, exactly what _rotOnly
documents. So the sit clip rides _rotOnly (drops Hips.quaternion) like every
clip; _rotWithHips removed. The seated read comes from the leg bends + a new
spawnRig({seated}) foot-replant (opt-in; standing spawns skip it).
- Drummer uses fleet.sitClip when present (retires SEAT_DROP), tags
fig.userData.procitySeated=true (F's gate). Stature ~1.1-1.3m in [0.9,1.5];
reads seated from the crowd POV (kit hides the legs). Graceful SEAT_DROP
fallback when sitClip absent (?noassets / before F wires {sit}).
- BYTE-IDENTICAL proof (load-bearing): with sitClip null AND loaded, keepers are
standing rigs (stature 1.718, innerRotY pi, replanted:false) and walkers 188/188
face travel. Parallel/opt-in path; _rotOnly + makeActor untouched.
- F seam: loadPedFleet(base, {sit}) — pass sit:<gig active> (false under
?classic → no sit.glb fetch, classic covenant intact). Default off, graceful.
Ledger #5 — backline unification. _addBackline plants E's guitar_amp at C's
stage.backline[0] (== D's R15 pose, 0.75m bass clearance) instead of a hardcoded
pose. C dropped its primitive ampStacks, so D owns the backline: placeholder-
persists (primitive amp → GLB swap) with the primitive as the ?noassets fallback.
Verified: GLB swaps, primitive under noassets, no crash, 0 fetch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The R13 backline plant sat down-stage (stage.z + 0.15), 0.29 m from the bass rig
— it interpenetrated the bassist (C's R14 audit caught it). Move it up-stage-right
to C's blessed pose (stage.x + w*0.36, deckY, stage.z - d*0.22): 0.75 m clear of
the bass, behind the front line, flanking C's primitive ampStacks. Pose-only.
Verified: draw/tri delta 0 (same GLB moved — pub 50 draws / ~183k tris, <=350);
?noassets unaffected (amp is fleet-gated dressing → 0 props, band placeholders,
no crash). No other code — continuity + the ratified sit-clip deferral are closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The last charter promise. A per-venue tonight roster (sim.js _venueRoster) of
who entered a gig venue tonight — surge occupants self-record in _enter, queue
admits relay via citizens.recordVenueEntry — is consumed by GigCrew.spawn: the
crowd's front slots BECOME the roster identities (same pedIndex, same height),
seeded strangers fill the rest.
- No golden move: both goldens are plan-gen hashes; each crowd slot is an
independently-keyed rng stream, so a roster override touches only that slot and
an EMPTY roster is byte-identical to R13 (verified). Roster heights 1.55-1.95m
stay inside the no-giants [1.4,2.0] gate. Pure bookkeeping: 0 rng, 0 GPU.
- Roster persists across interior exit/re-enter, bounded, auto-clears on the
setGig(id,false) transition (per-night). Deduped by stable key.
- queue.js: admitOne() returns {pedIndex,height,key} + fires an onAdmit callback
(auto-drain AND explicit) so F relays every walked-in punter to the roster;
_spawnAt stashes the identity. band.js: _make returns pedIndex; members carry
it for F's continuity smoke; crew.spawn is now null-gig-safe.
- Verified: surge 8/8 + queue 4/4 real identities land in the crowd; empty-roster
== R13; 7-night x 3-venue soak leak geo0/tex0, roster bounded, no identity
churn; interior 64 draws <=350 (0 new rigs); ?noassets placeholders, 0 fetch.
- F seam in LANE_D_NOTES: crew.spawn({roster}) + queue onAdmit relay + the
tonightRoster(venueShopId) accessor.
- Debt #2 (sit clip): investigated, deferred to v3.1 — the clip converts+binds
(tailnet Sitting_Idle.fbx -> 394KB sit.glb) but a real seated pose is ~1.2m
stature (fails F's no-giants floor) and needs a _rotWithHips loader + replant
(shared rig-stack) + an F smoke exemption; not a freeze-week change. SEAT_DROP
hack kept (reads seated from the money-shot front).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- RY audit (debt #4): the ped GLB fleet's visual front is +Z but the whole
system assumed -Z (sim heading math, C's pose ry, placeholder, impostor).
Fixed at the source (rigs.js buildFigure normalises every rig to -Z front)
and dropped band.js RY_FLIP. Corrects keepers/browsers (faced the back wall)
and streamed walkers (moon-walked): 223/223 gig + 188/188 flags-off now face
travel. Visual only, no golden move. keepers.js/placeholder.js/impostor.js
unchanged (fixed for free). Canonical convention -> LANE_D_NOTES for C.
- Instruments (debt #2): E's manifest GLBs swap over the primitives async
(placeholder-persists, fleet-gated -> 0 fetch under ?noassets), guitar/bass
held, mic + kit planted, guitar_amp dressing.
- Drummer (debt #3): 4-piece, seated at C's drums pose (sink + kit occlusion;
reads from the front/crowd POV - a sit clip is the polish).
- Outdoor queue (new queue.js): seeded 2-6 line at the frontage, drains on
admitOne() + auto-drain, consumes B's queueZone or a frontage-hugging door
fallback. F wiring seam documented in LANE_D_NOTES.
- Multi-venue surge (sim.js): setGig -> Set of concurrent venues.
- Interior gig 56 draws <=350; ?noassets+?gigs placeholders, silent-and-fine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow a ped into a shop and find them browsing. D owns occupancy truth; C owns browse points;
F wires the handoff. Validated end-to-end in-shell (?stock=real). qa --strict GREEN; v1 untouched.
Occupancy (D1): patronage records who's inside which shop by shopId. New
citizens.occupancyOf(shopId) -> {count, occupants:[{seed,enteredAt,pedIndex}]} (pedIndex = the
exact ped who ducked in, for identity continuity). Cleaned on emerge/chunk-drop/stream-toggle.
Deterministic: 17 shops + occupants byte-equal across two runs (fixed a stale-occupancy leak on
stream toggle).
Browsers: keepers.js gained browse:true (faces the shelf via ry, no player-greet) + pedIndex +
seedKey. Each = a merged ped ~1 draw. 12 enter/exit cycles: count == min(occupancy,3) 12/12,
worst 61 draws <=350 (C's headroom holds), leak-free (0 GPU delta after warmup), disposed every
exit via keepers.disposeAll().
Consistency (D2): inside peds hidden on the street (0 rendered), re-emerge at the door.
F handoff verified by running F's exact interior_mode wiring (enter -> occupancyOf ->
browsePoints.slice(0,min(count,3)) -> keepers.spawn). index.html setShops door points need
shopId:s.id added. Closing-time handled by the dwell model (pending A's explicit ruling).
Evidence: docs/shots/laneD/r9_browsers_in_shop.jpg. Changed sim.js + keepers.js + test page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The crowd comes alive. Default-on for the streamed roster (behind ?patronage=0); v1 path
(?roster=v1) untouched — no patron fields, golden identity holds. qa --strict GREEN.
Patronage (D1): per-ped state machine walking -> going (steer to a nearby open shop's door,
seeded ~every 10m within 18m) -> inside (hidden, 5-20s seeded dwell) -> emerge (resumes walk).
Hours-aware: noon = all types (up to ~38 inside, 7 types); night = video-only (the openLate shop
draws the crowd, roll ramps up as streets empty). Deterministic (150 chunk-keyed ids), leak-free
(0 GPU delta over the churn), budget-neutral (inside peds hidden -> draws -1).
Weather (D2): reads Lane B's PROCITY.weather contract (no import). setWeather({state,intensity}).
Rain -> density -56% (40-60% target), walk speed +14%, more sheltering (inside 6->25); overcast
-10%; clear = v1.
Also this session: R7 post-flip shell verification (no-flag boot streams, determinism, leak-free,
?roster=v1 restores golden roster) and pushed main + v1.0/v1.1/v2.0-alpha/v2.0-beta to origin.
F wiring (setShops door-point recipe, setWeather, ?patronage=0) documented in LANE_D_NOTES.
Only web/js/citizens/sim.js + web/citizens_test.html changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flag ?roster=stream stays default-off; v1 path byte-identical (citizen-0 = R4 golden, passes
F1 flags-off regression). Full evidence memo in LANE_D_NOTES for the R7 default-on decision.
- Soak (in-shell, real town): 242 chunk builds/disposes over 3 walks -> 0 GPU geom/tex delta;
16 shop enter/exit -> 0 delta; heap oscillates 50->77->54MB (GC, no leak); 188 chunk-keyed
identities re-derive from seed; 0 console errors.
- Hours-aware density: added setNightLivelyChunks + NIGHT_LIVELY_FLOOR so the open-late video
block stays lively at night (31->16) while ordinary streets go near-empty (65->6). F call-site
documented (openLate lot -> chunkKey via sim.chunkKeyAt).
- Composition with ?dig=1: roster is street-tier, inert while interior/dig open (frame loop is
street-branch-only); enter/exit record shops leak-free with stream on. No shared state with any
v2 flag -> all-on combo safe.
- Budget gap found + made safe: each decimated ped is 8 sub-meshes / 2 materials = ~7 draws/rig,
so full-density stream (perChunk 16) hits 356 draws (>300 street budget). Set default perChunk 8
(291 draws, budget-safe today). The clean unlock for full-density default-on = merge ped
sub-meshes by material (8->2 -> ~2 draws/rig -> perChunk 16 fits ~260); documented as R7 work,
F1-safe (v1 spawn view has ~0 near-rigs).
Only web/js/citizens/sim.js + web/citizens_test.html changed (+ NOTES/progress).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the R3 design note (was design-only). Default-off; the v1 whole-graph path is
byte-identical when the flag is off (shell citizen-0 signature matches the R4 golden exactly).
- chunk-local identity keyed (seed, chunkKey, i) — same seed + chunk -> same residents,
independent of town size / visit order; home edge picked from that chunk's edges.
- windowed spawn/despawn: hook-driven via Lane B's onChunkBuilt/onChunkDisposed (sim auto-
detects and stops polling) + a camera-poll fallback that needs zero Lane B changes.
- global near-cap + mixer stagger + rig pool + impostor layer unchanged, run across live chunks.
- owner-chunk despawn (no cross-boundary re-keying); per-chunk time-of-day thinning (hours-aware).
Proven on a 9x9 fixture grid: constant far-field density (~58-129 within 70m at spawn AND 150m
out, vs v1's uniform ~12-15); deterministic (507 chunk-keyed identities re-derive from seed);
leak-free (2 full grid walks / ~380 chunk cycles -> 0 geom/tex delta, pool capped 30); budget
unchanged (near-cap 24 + 1 impostor draw, 2.31ms/frame). qa.sh --strict GREEN.
Only web/js/citizens/sim.js + web/citizens_test.html changed. Flag + shell call-site documented
in LANE_D_NOTES for Lane F to wire web/index.html.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
D1: peds+keepers upgrade placeholder->rig in the shell; 12 enter/exit shops leak-free
(renderer.info.memory back to exact baseline); determinism holds across reload; ?noassets
fully placeholder with zero ped fetches.
D2 (gate-3 critical path): validated Lane E's decimated fleet — all 19 peds <=3k tris
(1564-2805, avg 2450), rigs bind+animate (skinning/skeleton preserved), 0 broken meshes under
eviction churn, silhouettes+identity intact, atlas bakes clean. Near fleet ~59.6k tris at the
24-cap (was ~1.2M) — F clear to re-measure gate 3.
D3: fixed a real bug found only in-shell — the impostor self-tone-mapped (toneMapped:false +
in-shader ACES), correct on the direct-canvas test page but DOUBLE-tone-mapped under the shell's
EffectComposer/OutputPass. Reworked to a standard toneMapped:true material outputting linear via
three's own tonemapping/colorspace chunks, so it matches the near rigs on both render paths.
setExposure is now a no-op (exposure is global). Verified noon+night in-shell.
Only web/js/citizens/impostor.js changed (+ NOTES/progress/R4 shots).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fable round-3 §Lane D (all three tasks):
- sim.js: add CitizenSim.setExposure(e) passthrough so mid-tier billboards track the
shell's per-segment renderer.toneMappingExposure (lighting.js animates it); stored so it
survives the placeholder->rig atlas re-bake. Verified: uniform tracks 0.55<->2.3 and
persists across the fleet upgrade.
- Keeper GLB-rig upgrade contract verified leak-free (my side): 15 enter/exit cycles with a
fleet-backed rig keeper leave renderer.info.memory geo/tex exactly at baseline. Enabling it
in the shell is one arg (pass `fleet` to KeeperManager) once F's §3.4 lands.
- LANE_D_NOTES: exact Lane F call sites for setExposure + keeper fleet-upgrade, and the
chunk-streamed roster v1.5 design note (chunk-local identity, windowing, ownership/hand-off,
budget, opt-in migration) — design only, no implementation.
Code touches only sim.js; no sibling files edited. Determinism still passes, near cap 24,
mixer 0.3ms, no console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Populate the town at city scale, on budget, deterministic per-citizen identity:
- rigs.js — ported 90sDJsim rig stack: mixamorig canonicalisation so one shared
walk clip drives all 19 peds, head-bone height-normalise, feet-plant, pooled
near-tier actors with walk<->idle crossfade
- impostor.js — 4-yaw sprite-atlas baker + instanced billboards: whole mid crowd
in 1 draw call, tone-matched to the ACES near rigs
- sim.js — deterministic roster, footpath lanes off the street graph, near/mid/far
LOD with hysteresis + hard 24-cap, staggered mixer budget, time-of-day density
- placeholder.js — seeded low-poly box humanoids (frame-one population + ?noassets)
- keepers.js — one keeper per shop at the counter, idle + greet-turn
- 21 ped GLBs (byte-identical from 90sDJsim), citizens_test.html harness,
LANE_D_NOTES.md, 5 beauty shots
Verified in-browser: 200 citizens at max mixer 0.2ms (budget 2ms), near capped 24,
determinism holds (200 identities match seed), no T-pose, ?noassets crash-free.
Consumes Lane A plan.streets with zero adapter. Six adversarial-review bugs fixed
(non-deterministic fleet order, shared-resource disposal on pool eviction,
impostor colour-management).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>