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>