PROCITY/docs/REVIEW_CITY_PATTERNS.md

36 KiB
Raw Blame History

PROCITY — CAN WE ADD MORE PROCEDURAL CITY PATTERNS?

A decision-grade review, post-v7.0. Written for Fable, addressed to John.


1. THE STATE OF THE TOWN

Seven epochs in, the machinery is genuinely good and the street is genuinely empty. Both halves of that sentence are load-bearing.

What is strong. The plan/runtime split holds: Lane A is 2,546 lines of pure data with no THREE import, no network, no clock, JSON-round-trippable, under 100ms per plan, running 156,352 assertions in 20.6s all green. The determinism law is not aspirational — seedFor(citySeed, kind, id) hashes a semantic string, so a new kind label is a mathematically independent stream by construction. You can add five hundred draws to a new layer and every existing tree, shop and band name is bit-identical. That single property is what makes this entire review possible. The classic covenant is machine-enforced (fingerprint 0x5f76e76, pinned independently in two files). The zero-asset law is tested by counting network requests, not by asking the code. There is exactly one money seam in the whole game. The asset factory has cost $0.64 in its entire history.

What is genuinely thin. The street has objects and no affordances. That is the whole gap, wearing ten different hats:

  • The food cart is unmanned. 44 cart sites on the golden seed, 511 placed across the 21 real towns. furniture.js:252-255 holds the full shop record — id, type, lot, rotation — at the moment it pushes the cart's matrix, and throws all of it away. No vendor, no price, no hours, no transaction, no smell of onions. It is 15× more common than the record shop across the real corpus.
  • The bus stops have no bus. busShelterStops(plan) was exported in R6 with a comment saying it exists "so the future vehicle loop can enumerate stops without touching furniture internals." The vehicle that arrived was a tram running the high street. Three consumers read it — the tram, the light pools, the debug overlay — all as coordinates. Nobody ever waits at one.
  • There are no animals. Grep-verified across all of web/js: magpie, kelpie, kookaburra, wombat and galah exist only as tokens in the shop-name generator. The town is named after wildlife it does not contain. No cars either, apart from the tram.
  • The corridor law is published and contradicted. roadWidth/vergeBand/poleOffset are exported from registry.js, obeyed by exactly one consumer, and disagreed with by every other lane by about nine metres. Measured on the golden seed: all 254 bench points lie outside A's verge band and 138 of them land inside a lot footprint; 792 of 792 sampled ped-lane points lie outside it and 521 land inside a lot. ground.js:55 paints the entire 28m corridor as bitumen.

THE ONE STRUCTURAL OBSERVATION. Lane A publishes contracts the runtime lanes do not consume, and each runtime lane carries a private duplicate. There are four implementations of isOpen, two incompatible chunkIndex functions (A buckets a lot into every chunk its AABB touches; B buckets by centre only), two shop registries (B's is missing all three venue types, which is why every gig venue in every town has rendered op-shop sign colours since R13), and a chunk lifecycle hook (ctx.onChunkBuilt) that is built on both sides and wired on neither.

The visible cost of that, today, on the boot every player gets: all 14 gig posters are standing in the road. A's placer obeys poleOffset (6m). B's ground paints bitumen to 14m. A's own selfcheck validates clearance against roadWidth/2 = 5m, so the gate is green while the street is wrong.

Every street-side content idea in this review — a bus stop with a real platform, a vendor pitched on a verge, a dog on a nature strip, a parked car at a kerb — collides with this before it collides with anything else. The highest-leverage change available is not new content. It is deciding where the kerb is. It costs about twenty lines and zero draws.

Two corrections to house folklore, both measured. First: the draw budget. The "~131 draws / ~185 worst street view, roughly 100+ draws of headroom" figure is a teleport-to-bookmark number and it does not survive a walk. Walked in 8m steps down the main spine, the default boot peaks at 280 draws of 300 (R37 re-measured and PINNED it at 292 draws / 124,793 tris; the 280-vs-191 split was camera bearing, not method — see LANE_F_NOTES §37). The real margin is about twenty. Triangles, however, are wide open — 99k124k of 200k, with the two independent measurement sessions disagreeing enough that the tri baseline needs a re-pin. Spend triangles, not draws. A town-wide single InstancedMesh costs 1 draw regardless of population (venue.js runs 381 light pools that way, verified); a new per-chunk instanced type costs +1 draw × up to 31 live chunks.

Second, and larger: all 21 real towns are 100% use:'shop' lots with exactly one district, mainstreet. No house lots, no yard lots, no stall lots, no market block, no residential backstreets. The town the design language describes — the market square, the backstreets, the yards — exists on precisely one town: the synthetic one, which is also the classic one. This kills or halves a third of what was proposed, and it is the most important strategic finding in the packet.


2. THE ASSET PIPELINE, HONESTLY

Everything below was measured on this box today, not read from a doc.

Class Path Cost Licence Blocker
Audio gen_audio.py — 781 lines of pure numpy seconds, $0 🟢 original None. Best class we have.
Skins / signage flux_local on-device 9.3s each, $0 🟢 None
Static prop flux → bg_remove_localtrellis2_mlxnormalize.py ~2.53 min, $0 🟢 CC0-equiv None. ~2025 props/unattended hour.
Vehicle (static) same ~3 min, $0 🟢 Greenfield; wheels won't turn
Rigged biped trellis2_mlx → MIRPAMO autorig → retarget ~5 min 🟢 Feed GLB/BVH, never FBX
Rigged quadruped HARD WALL
Rigged bird HARD WALL, worse

The pipeline is one generation behind the farm. pipeline/gen_props.py hardcodes trellis_mac (p50 245.4s, 33 jobs in the last 7 days). The farm's actual workhorse is trellis2_mlx — 384 jobs in 7 days, p50 134.6s, better PBR bake. That is a ~2× throughput gain for a one-line change and it should land with the wave. blender_convert has 3 lifetime jobs with 1 failure and has been untouched for three weeks — keep it off any critical path. Budget 45% retry on mesh gen; failures are opaque (exit 2, one OOM-kill).

Two brief premises are wrong. Cloudflare Workers AI is live and fast (2.54s mean at 1024²) but the "~10,000 free generations/day" figure is 10,000 neurons per day — a compute unit, not images. The real ceiling is plausibly hundreds. Plan CF as a fast-path accelerator, not a budget. Its output is also JPEG with no alpha, so bg_remove_local is mandatory before any mesh step, and its framing is unreliable without procity's own tuned prompt template. Separately: MOCAPGOD does not exist on this box~/Documents/MOCAPGOD is 0 bytes, one empty queue/ dir, not a git repo. The working substitute is MODELBEAST motion_local (MoMask, text→BVH, ~11.7s, 4/4 success) — human-only.

THE ANIMAL BLOCKER, stated once and clearly. MIRPAMO cannot rig an animal and this is architectural, not tuning. Its landmark detector assumes "upright character, +Z up, roughly symmetric about X=0, T- or A-pose" and hardcodes anchors as fractions of a biped's height (groin 0.52, chin 0.87, knees 0.285). build_skeleton() emits a fixed 14-call humanoid chain. A repo-wide grep for quadruped|animal|dog|horse|bird|tail returns zero hits. The only bonemap shipped is human CMU mocap. MoMask is HumanML3D — human. The clip bank is a single mixamorig namespace.

Worse: a non-humanoid does not fail, it silently does nothing. Lane D pushed the bone-free food-cart GLB through the real rig machinery. buildFigure did not throw; the bbox fallback scaled it to 1.75m and planted it; makeActor returned hasClips: true while 0 of the walk clip's 64 tracks bound. A dog on the walk graph would glide down the street frozen in bind pose and no gate in the repo would notice. The impostor bake compounds it — it frames a square cell at 1.14× subject height, so any quadruped is clipped in the mid tier.

So: animals are static props, scripted transforms, or vertex-shader fakes. Forever, until a human hand-rigs a quadruped in Blender. Every one of the six animal proposals reached this conclusion independently, and that convergence is the single strongest signal in the review.

REUSE BEFORE YOU GENERATE. Nine published, budget-legal, provenance-clean assets are sitting idle in the manifest with zero runtime consumers:

  • novelty_record — 8,000 tris. Lane E decimated it from 26,486 in R5. furniture.js:275 already calls glb('novelty_record', …) but GLB_FILES has no key, so the call can never fire. A stale comment is the only thing keeping a finished hero landmark prop out of the game.
  • streetlight — 4,999 tris, no consumer; run decimate_props.py (the R22 tool that took the bin 3,000→800) and wire it.
  • Four bench variantsbench_modern 640, bench_wood 3,999, longbench 1,172, park_bench 5,000. Every bench in all 23 towns is currently the same mesh. longbench at 5.86m is explicitly the arcade/market feature bench the audit already describes.
  • coffee_table 495, box_crate 78, counter (the 4m balcao, 276 tris — which is exactly the bar_counter fitting that is still primitive-only in all three venue types).

That is nine assets, one line each, zero generation, zero licence work. Do this before generating anything.

DO NOT reuse: the 3GOD depot as a pool (971 live assets, mixed provenance, known trademarked items inside; _published.json records 53 with no drift, but AUDIT.md records 138 — a ~7× stale cross-reference). And shop-cat.glb is not a reuse — it is an intake: 8,000 tris, zero entries matching "cat" in _published.json, needing a decimate, a publish record, an AUDIT row and a licence ruling. A fresh local gen is cleaner and costs three minutes. The hardyards set (39 GLBs — gum, jacaranda, fence panel, gate, shed, pergola, washing line, water tank) is exactly the vernacular kit's shopping list and its MODELS.md documents provenance for one file. Do not ship it; do clear it, separately.


Twenty-seven proposals went to adversarial review. One survived unqualified. Where the reviewer corrected the designer, the corrected version is what appears below and I say so.

WAVE 0 — THE FLOOR

All micro. All must precede content. Roughly one round across three lanes.

0.1 — THE KERB RULING, and the widened footpath. (Lane A ruling + Lane B code + Lane F gate) Adopt A's published corridor law. Do not paint grass on high streets — registry.js:220's own comment calls that 9m band "verandah'd footpath", and buildings.js:458-472 already builds an awning over it on every shop lot. Instead extend the existing footGeos merged class inward from e.width/2 to roadWidth(e)/2. +0 draws (same merged mesh), ~2k tris, fill-rate improves because 18m of every main street stops being road texture, and the 14 gig posters land where A already thinks they are. Must special-case arcade (roadWidth 0 → zero-area road quad and coincident kerbs) and lane (vergeBand = [2,2]). Gate: the falsifiability control the reviewer called the best design in the packet — run the poster-clearance leg against ?verge=0 and assert it FAILS there, because the pre-change failure is already known and counted at exactly 14. Grass verges survive, but only on residential edges, which is where they are period-correct and where 1.4 wants them anyway.

0.2 — REPAIR smoke_tram. (Lane F, micro) flags_check.py:617-630 asserts exactly one thing — !!getObjectByName('tram') — prints a SKIP and returns when absent, and passes on the fenced path because tram.js:110-114 adds a named empty group. Five of seven transport proposals leaned on this as the "cheap proof" for a refactor. It is a live violation of the house's own vacuous-gate law inside the house's own suite. Fix it regardless of what ships.

0.3 — RE-PIN THE BUDGET OF RECORD. (Lane F, micro) Adopt the walked worst frame as the number the law measures (clean-load bookmarks under-report by 6080 draws — a walk carries the R+1 dispose window at up to 31 live chunks vs 22). Re-measure once on a real GPU; the headless numbers are SwiftShader. Settle the shared standing-layer instance cap once — three animal proposals independently specified 120, 250 and "plus gulls" for the same layer.

0.4 — WIRE ctx.onChunkBuilt / onChunkDisposed. (Lane B, one line) Producer at chunks.js:43-51, consumer at sim.js:203-205, both shipped, never connected. Every per-chunk entity system in the slate wants it.


WAVE 1 — INHABITATION

Four items. Every one is a single round. None needs a street verb, a rig, a new clip, or John's mocap footage. Three of four are +0 or +1 draws. They touch no shared files, so all four can run in parallel.

1.1 — SOMEONE ELSE'S LIFE: the audio inhabitation layer. (E + B + F) The only unqualified SHIP in twenty-seven proposals. The manifest ships 7 ambience / 9 sfx / 7 music and not one animal, domestic or seasonal sound. A dog going off behind a paling fence, a mower two streets over, a radio through a window, sprinklers, a roller door — the sound of a life happening off-screen. Mechanism: seeded point-source audio anchors, each a ~30-line numpy generator in gen_audio.py's PACK registry, deterministic via crc32(name). Corrected: it is six new mixer layers, not "generalising audio.js:300-311" — audio.js has hardcoded bed layers per source class, not a generic point-source system. And there is no panning: playSfx is mono with a gain multiplier, so distance works and direction does not. Say so or budget a StereoPannerNode. Derive day-of-week locally rather than depending on the calendar item. Budget: zero draws, zero tris — the only item in the slate that cannot touch the budget under any circumstance. Audio pack 10MB → ~11MB against a 25MB cap. Gate: keep the falsifiability control verbatim — point one table entry at a non-existent key and assert the leg FAILS. It is the correct descendant of the R23 lesson and every other gate in the slate should copy its shape.

1.2 — TWENTY-THREE TOWNS: the per-town character vector. (B + E + F) Twenty-three real towns currently differ only in street layout and shop names. Give each a ground palette, an ambience mix and a sky bias. Mechanism: ground.js's add() merges each surface class into ONE mesh for the whole town regardless of size — verified, exactly 5 meshes. A palette swap is +0 draws / +0 tris. The strongest cost claim in the entire packet. gravel(verge) and reddust(outback) already ship in the manifest and are selected by nothing. Corrected: the state field is not populated on all 23 — newtown_godverse and redhill_godverse carry state: '', so the vacuous arm must be per-town, not per-corpus, or those two silently take a default branch with no SKIP printed. The palette is 10 skins with one outback and one gravel, so "Darwin is red dust, Hobart is cold light" needs 34 new gen_skins.py generations — name them in the charter. And skyBias must not go through lighting.setSky: externalSky (lighting.js:53) is a one-way latch with no un-set path. Feed it into the base skyName selection instead. townCharacter(null) returning the frozen literals makes classic byte-identical by construction rather than by flag — the right mechanism. Budget: +0 draws / +0 tris.

1.3 — THE MAGPIE. (B + E + F) Swooping season. For six weeks of the game's year, one bird on one street will not let you walk past it. Real behaviour: a ~6-week window AugOct, north to south, and ~90% of males never swoop — so one bird, not a plague. Mechanism: a static mesh on a scripted parabola. No AI, no flocking, no pathfinding — the tram's verified 2-mesh/2-draw pattern at a fraction of the cost. Wing wave through wind.js's amplitude-uniform shape, which is byte-identical at amp 0. Corrected: cut the ice-cream-container hat. There is no street verb — index.html:493-499 binds only ][ P M T, and the only street affordance is a click that enters the shop. "Buy the hat at a milkbar door" either enters the milk bar or does nothing, and the gate would have to call the wallet directly, which is the R31 dig lesson recurring. Also scale the territory with the road graph: one bird among bendigo_real's 13,730 tree anchors across 2,593 edges is statistically unencounterable. Budget: +1 draw / ≤900 tris. Gate: keep "the day-roll clears the latch and localStorage gained no new key" verbatim — the best single arm in the packet and a genuine delta-law falsifier.

1.4 — BACKYARD AUSTRALIA: the residential district exists. (B + E + F) Hills Hoists, paling fences, TV aerials, water tanks. 181 house and yard lots on the default boot with nothing in them. Corrected, and cheaper than proposed: build it inside buildings.js, riding the existing boxMats InstancedMesh (buildings.js:311 already carries shells, parapets and verandah posts, and buildHouse already pushes into it). Hoist, fence run, aerial, tank are all boxes → +0 draws. Only the alpha washing quads need a new mesh. +1 draw/chunk total, not +2. A separate world/yards.js cannot reach boxMats and would have to pay the draw the proposal then presented as its honest cost. The washing needs an inverted wind weight. wind.js:38-45 computes wH = clamp(y/4.4,0,1); wH *= wH — top-weighted, base planted. Correct for a gum tree, exactly wrong for a sheet pegged at the top. At 1.6m the amplitude is 0.74.5cm, i.e. visually inert. Either drop the washing this round or write an inverted-weight sway mode. Its own named risk is now resolved by measurement: the residential district walks at 157 draws / 31,651 tris worst (edge 16, 20 live chunks) against a main-spine control of 263 / 123,602 on the same build. ~143 spare draws where these props land. REJECT the hardyards GLBs on provenance grounds — this was the best licence judgement in the packet and it should be quoted into the charter. Scope honestly: this is a synthetic-town feature. That is still the default boot, but it is one town.


WAVE 2 — THE STREET GETS A VERB

Sequential. Wave 2.0 is the substrate that four separate proposals each tried to invent privately.

2.0 — THE STREET INTERACTION SURFACE. (B + F, half a round to a round) A KeyE verb on the street; an interactRects registry with a kind tag resolved off instanced meshes by instanceId; explicit hit priority (door beats cart); a click fence. Two listeners currently ride the same canvas click (hud.js:376 and index.html:510's pointer re-lock), so any cash-spending click must be gated on player.isLocked. Also the street-mode dig frame branch, which the shell does not have.

2.1 — THE MANNED CART. (B + D + F, one round after 2.0) Consumes the theme's flagship dead seam. KeeperManager is genuinely not interior-coupled and costs a measured 1 draw and 2,391 tris per stationary rig, 0.02ms of mixer, disposal residue-free. The blocker, which is not cosmetic: the proposed "+0 draw" doorRect ride would make every milk bar and stall un-enterable from the front. hud.js:317-327 takes hits[0] — the nearest intersection — then resolves by distance to the hit point; the cart sits 1.55m proud of the door on its own centreline. That is 44 shops on the golden seed and 589 across the caches. The fix is to raycast the cart's own InstancedMesh (already emitted at furniture.js:276) and carry the shop record that furniture.js:252-255 already has in scope and discards. Gate it by standing in front of a milk bar and asserting the resolve names the milk bar. Cap at N=3 nearest, asserted while walking a dense block, not at spawnkeepers.js:67-90 ticks every mixer every frame with no LOD. Budget: +3 draws / +7.2k tris at the cap.

2.2 — THE FAUNA SUBSTRATE + the field guide. (B + F + E, multi-round) One shared town-wide standing layer at +1 draw / ~240 tris, one settled cap, one save-backed sighting ledger. The ibis on a bin is the proof species; the perched cockatoos, the gulls, the tied dog, the possum, the moths and the two flies all ride the same layer once it exists. Build it once, properly. Corrected: add sightings to adopt() (save.js:166) in the same commit as payload() — adopt is the travel path, and a reload-based gate would not catch the bug. Cap and dedupe (16 species × 23 towns = 368 natural ceiling). Measure the selection path on bendigo_real (23,744 roost anchors), not katoomba. It is three lanes, not one.

2.3 — DOGS, TIER B FIRST. (B + E + F) The kelpie tied outside the shop, as one global InstancedMesh at +1 draw. Tier B anchors off shopfront frontage, which exists on all 1,117 real-town shop lots. Tier A (the fence bark) must be re-anchored — it keys off use === 'house'|'yard', which exists on zero real towns, and its gate only ever visits the one town where the subject exists. Fold the bark into 1.1 (the audio layer) where it belongs.


4. THE CUT LIST

The house kills ideas with measurements. These are the measurements.

Cut The measured reason
The town-loop bus Its own precondition-as-assertion goes red. It asserted the tram is fenced on newtown_real; measured, newtown is not fenced and the tram runs 16 stops over 1,924m. Fitzroy has 139 shops (not 160), is not fenced, and runs 28 stops over 4,101m. The mutual-exclusion rule builds the bus on 7 of 23 towns and those are the sparse ones (darwin 12 shops, braddon 19, bowral 30). The shelter-rich towns are overwhelmingly unfenced: northbridge 272, adelaide 209, newtown 149. What survives is one filetram.js:93-97 files the real unbuilt thing: real high streets come out of OSM as side. Extend spinePolyline's edge set to main+side and let the tram run on those 7 towns. +0 draws, +0 tris, one lane, one round, same dead seam retired.
The ute that sells Placement set empty on all 21 real towns. And 36 of its 83 synthetic candidates — all 36 yard lots — have no shop on their frontEdge, so its own save-poisoning fix fails on 43% of its set. A borrowed shopId is also a user-visible lie: hud.js:159-163 renders the find as bought at the milk bar next door.
The charity bin REJECT. On the default boot the only collectibles are parody records, every parody sleeve carries a band, BANDS.record's lowest low is 2, and sellOffer(2) = 1 — never 0. The filter accepts nothing. The synthetic town has 77 record shops so the "no counter in this town" arm never fires either. Its accept-arm has to inject an item no shipped code path can produce. Vacuous by the house's own definition. Also four lanes plus an asset round, not "micro".
The dusk flush The day is 240 seconds long (lighting.js:83, 6 segments). "The six o'clock racket" is a fifteen-times-an-hour racket. And its own mitigation is unimplementable — the procity:segment event carries {seg, hour, night} and is dispatched identically from update(), setSegment() and stepSegment(), so a real transition cannot be told from a bracket-key step or a screenshot bookmark. Keep the perched birds; a proximity startle is the better trigger anyway.
The magpie hat No street verb exists.
The level crossing as an OSM feature Needs a 23-town cache rebuild — build_towns.py rewrites the whole cache, and its own docstring records the Newcastle incident where shops were refetched and old roads silently reused. 46 pinned goldens ride on byte-identity nobody has proven. Four strictly-ordered lanes, and the gate's only realistic state is SKIP until E ships. Shelve the real-rail version. The rescoped version — seeded synthetic crossings, procedural geometry, gen_audio bells and horn and rumble — is one round and still shuts the map for twenty seconds.
The mail run Its headline mechanic cannot exist without a fourth save-schema widening, which the proposal claims it does not need — resolveAuctions resolves strictly listDay < day. The conservative fallback is one prop and one string. Weakest 1990s-Australian specificity in the set: a period-correct object with a generic mechanic. Keep the one-hammer analysis as a gate arm for anything that touches GodBay.
Parked cars, this epoch Deferred, not rejected. Blocked behind the kerb ruling (a car at roadWidth/2 sits 9m inside painted road), a chunk-bucketed dynamic collider registry that does not exist, and a BASE_DRAWS=164 re-pin. +48,000 tris always submitted — a town-wide instanced layer computes an instance-aware bounding sphere and is effectively never frustum-culled. Affordable but the likeliest item to eat the margin. When it lands, make the 45° angle parking a charter requirement — it is doing all the Australian work.
Market day / boot sale / garage sale as pan-corpus features All three are synthetic-town-only (100% shop lots, one district). Market day additionally needs a geometry path that does not exist — ground.js's rectGeo is axis-aligned only — and would pave 27,600m² where the stalls occupy ~44×77m.
Anything with a rigged animal Capability wall, not a design failure. See §2.
The research doc's trap list Endorsed wholesale: full traffic sim, civic interiors, per-NPC schedules, boids, real-date seasons, terrain sneaking in sideways, 23 bespoke Big Things, voice lines, brand fidelity, multiplayer, needs models.

And the plain answer to the three you named. Street vendors is the strongest — but only the manned cart, and only after the door raycast is fixed; the rest of the theme is synthetic-only or rejected. Stray animals is viable but permanently capped at static and scripted — and the magpie is nonetheless the best charm-per-byte item in the entire packet. Public transport is the weakest of the three, and its flagship proposal refuted itself with its own gate. All three were beaten by things you did not name: the audio inhabitation layer (zero draws, the only clean SHIP), the per-town character vector (+0 draws, +0 tris), and the kerb ruling (twenty lines, and it is what makes every other street idea geometrically coherent).


5. THE ASSET WAVE

The proof batch — one unattended afternoon, $0, decisive

Four capability questions are actually open. This answers all four.

  1. magpie_static — flux_local → bg_remove_local → trellis2_mlx → normalize → decimate to ~900 tris. Proves the corrected mesh path end to end on an operator procity has never used, and it is the one asset wave 1 cannot ship without.
  2. hills_hoist — same path, target ≤600 tris. Deliberately the hard case: thin structures (wire, arms) are the known TRELLIS failure class. If it fails that is a decisive finding, and the fallback is primitives, which boxMats wants anyway. 35. magpie-clack, dog-bark-fence, mower-distant — three gen_audio.py PACK entries. Minutes, $0, deterministic, parody-safe by construction. 67. ground-grass-dry, ground-grass-temperategen_skins.py --local with a new GROUND template, ~5.7s each.

Plus the one-line pipeline fix: swap trellis_mactrellis2_mlx in gen_props.py (~2× throughput), and correct the CF credential path in the fleet skill (it points at ~/Documents/backnforth/.env, which does not exist on this box; the working client reads ~/Documents/fluxgod-work/.env).

The full wave, by class and order

First — reuse, zero generation: the nine idle manifest assets from §2. novelty_record, streetlight (after a decimate_props.py pass to ~800), four bench variants, coffee_table, box_crate, counterbar_counter. One line each.

Audio (gen_audio.py, $0, 🟢 original, ~30 lines each): magpie clack + carol, cockatoo screech, dog bark (fence + near), mower, sprinkler, radio-through-a-window, hammering, roller door, letter-slot clang, crossing bell, distant train horn, rail rumble, tin-roof rain, cicadas, wind-in-gums. Plus the six missing footstep surfaces — grass, gravel, red dust, brick pavers, lino, timber verandah. The manifest ships ten ground skins across six use-classes and only two footstep sets exist; walking on red dust currently sounds like concrete. Lane E's own map calls this the cheapest measurable content gap in the repo.

Skins (flux_local, ~5.7s, $0, 🟢): the 34 ground palette gaps (dry summer grass, cool-temperate grass, red-dusted bitumen, coastal sand) and a FOR-LEASE / whitewashed-glass texture. The ~20 missing shop-type facades — servo, church, bank, post office, butcher, bakery, chemist, newsagent, TAB, laundromat, barber, hall, motel, station — are a wave of their own and belong with the vernacular kit, not here.

Meshes (~3 min each, $0, 🟢 CC0-equivalent under AUDIT.md:101's doctrine — flux_local concept on an Apache-2.0 model, TRELLIS reconstruction of that concept = original work): magpie, cockatoo/galah (one body, three tints), silver gull, tied dog, possum, cat (generate ours — a fresh local gen is cleaner than an intake from a mixed-provenance depot with no publish record, and costs three minutes), phone box, paper stand, pillar box, A-frame board, milk-crate stack, wheelie bin, trestle table, cardboard box, transformer drum. Roughly fifteen meshes ≈ one unattended hour at the measured rate. Budget 45% retry.

Rigged: nothing. The vendor reuses the 24 shipped bodies and 8 shipped clips. If a "hand over" clip is wanted later: motion_local (text→BVH, ~11.7s) → MIRPAMO retarget → fbx_to_clip. Human-only by design.

Licence discipline. Every mesh, skin and audio asset above lands 🟢. Each carries a named source zone in LANE_E_NOTES, a generation method naming the exact tool and model, an AUDIT.md narrative entry with measured tris/footprint/height, and a _published.json entry if it touches the depot. hardyards stays 🟠 until cleared. The 971-asset depot stays 🔴 and is not a pool. Re-run the AUDIT depot cross-reference before anyone plans reuse from it.


6. THE SEQUENCING

Epoch name: v8 — SOMEONE LIVES HERE.

(The research doc proposed "THE TOWN HAS A WEEK". That name over-promises now — the calendar and trading-regime item was rescoped into three rounds because the per-type shut list cannot be a uniform: shop hours are baked into a per-vertex aHours attribute at chunk-build time, so a per-type close delta means rewriting that attribute across up to 27 live chunks on every sleep. Its "+0 draws, +0 tris, +0 bytes" claim must not reach a charter. It is a good v9 item; it is not a cheap one.)

Against what is already chartered. v6 LIVING STREET is chartered and gated on your mocap footage. Nothing in wave 0 or wave 1 needs a clip. That is deliberate and it is the main argument for this shape: it gives the project a full epoch of parallel work that does not wait on your camera.

Against the frontier shelf. Terrain — nothing here needs Y; keep it that way. The vernacular kit — wave 1.4 (backyard Australia) is its residential half arriving early; the ~20 missing shop facades are its other half and stay on the shelf. The civic layer — this is the real v9. Every real-town gap this review found points at the same place: plan_osm.js emits one district and the Overpass fetch asks for only shops and roads. 30,959 footway ways, 12,351 service ways, 6,421 crossings, 2,714 sidewalk tags and 13,199 oneway tags are already cached in-repo, unused, and a bounded third fetch class (marketplace, park, pitch, memorial, fuel, school, bus_stop, telephone, post_box) is mechanical under the standing authorisation. That is the fix for "real towns are a shopfront strip", and it is a better-motivated epoch than anything in the vendors theme. Horizon matte / Voronoi outskirts / sky domes — orthogonal, but 1.2's skyBias must be designed knowing sky domes are coming, and must not touch setSky's one-way latch.

WHAT HAPPENS FIRST, plainly: the kerb ruling. Not code — a decision. Until it is made, five separate proposals are designing against contradictory geometry and fourteen gig posters are standing in the road on the boot every player gets. Make the ruling, land the footpath widening with the ?verge=0 falsifiability control, repair smoke_tram, re-pin the budget — then wave 1's four items run in parallel across four lanes with no shared files.


7. WHAT NEEDS JOHN

Decisions

  1. The kerb ruling. Footpath or road, for the 9m band between kerb and shopfront. Recommend: footpath — adopt A's published law, widen the existing merged footpath class inward, +0 draws, and the posters land where A already thinks they are. Grass verges on residential edges only.
  2. Is synthetic-only acceptable? Backyard Australia, market day, garage sales and the whole residential/market vocabulary live on one town, because all 21 real towns are 100% shop lots with a single district. Either accept "the default boot is the richest town", or fund the civic-layer fetch that gives real towns their other lot uses. This is the biggest strategic question in the review and it is not a lane's to answer.
  3. The animal answer. No rigged quadruped exists or can be automated on this fleet. Accept static + scripted-transform animals for v8, or commission a hand-rigged quadruped in Blender as a one-off human job. Recommend the former now, defer the latter.
  4. The market plaza. ground.js paints a 46×46m plaza at the origin — on top of the spine road, touching none of the 40 stall lots at x 77..33, z 100..23. Fixing it changes visual output on the covenanted town. Recommend: fix it under the amendment law (the R27 precedent — the covenant is anti-drift machinery, not a defect preservative), as its own small ruling, not folded into a content round.
  5. Epoch name and shapev8 — SOMEONE LIVES HERE, wave 0 then four parallel wave-1 items, wave 2 behind the street verb.
  6. The budget of record and the shared-layer cap — adopt the walked worst frame; settle one instance cap for the fauna layer before three implementations invent three.
  7. hardyards provenance. 39 GLBs that are exactly the vernacular kit's shopping list, with provenance documented for one file. Only you know where they came from. Worth an hour of your memory.

Credentials and authorisations

  • Nothing in this slate is blocked on a credential. MODELBEAST is up and token-authed from disk; Cloudflare works; the depot accepts passwordless tailnet uploads. Worth stating plainly, because the brief assumed otherwise.
  • Mixamo fetch needs your logged-in Brave session for any uncached clip. Not needed for this slate; needed if anyone wants a new human clip that MoMask cannot produce.
  • Standing authorisation to re-run the Overpass fetch — 23 towns, one bounded query each, 4s courtesy, ODbL attribution into every cache and into SOURCES.md. Not needed for v8 as scoped. Needed for v9.
  • Where is MOCAPGOD? The brief says lanes 0/A/B are done. On m3ultra the directory is 0 bytes and not a git repo. Either it lives on JING5 or the Air, or it was never started. Not blocking — motion_local covers the need — but the fleet docs are wrong somewhere and only you can say where.
  • A ruling on the depot. 971 live assets, 53 in _published.json, AUDIT.md recording 138, and known trademarked items inside. Recommend: treat it as a read-only archive, generate fresh for anything new, schedule an AUDIT.md re-run.
  • Optional but cheap: keep ?verge=0 permanently as a gate flag. It is the best gate design in the packet and it costs nothing.