Go to file
m3ultra 1eb0b45bd0 Lane A round 24 (v5.0-alpha): the identity field — godverseShopId, published first (ledger #2)
The optional shop key that resolves `stock_godverse/<godverseShopId>/`. Schema STAYS v2 (optional field ⇒
every shipped cache still valid, the 21 osm caches untouched by construction). Published to
web/assets/towns/README.md — the doc G and E build to — plus CITY_SPEC. selfcheck ALL GREEN (+15 checks),
synthetic 0x3fa36874 frozen, NO golden moved.

THE BRIEF'S "THE POS SHOP ID IS THE ID — NO MAPPING TABLE" IS FALSE FOR THE ONE SHOP THIS ALPHA IS ABOUT.
G's own docstring says it: Monster Robot Party is NOT in thriftgod's census, so its id is its DEALGOD STORE
ID 3962749, while census shops use thriftgod ids (max 2992) -- two disjoint spaces, deliberately kept clear
of each other. Had I coded "godverseShopId == shop.id" or derived it from the census, the headline shop
could never be keyed. So it is an OPAQUE key: no derivation from `id`, no equality assert, no mapping table
(the R12 gigKey law -- one key, zero mapping; G emits, everyone reads).

THE FIELD HAD TO SURVIVE THE LIFT OR IT WAS DECORATION. plan_osm builds a FRESH shop object and copies
nothing unknown, and the runtime reads plan.shops[], never the cache -- so a cache-only field could never
reach F's per-shop base. It's now carried through, CONDITIONALLY: absent ⇒ the key isn't written ⇒
JSON.stringify byte-identical ⇒ every pinned golden holds until G re-emits (that re-emit IS the sanctioned
move). Proven end-to-end: a keyed cache puts `stock_godverse/3962749/` on the plan with census ids
(101..107) coexisting alongside it.

ARMS SPLIT BY HAZARD, NOT TIDINESS:
- malformed  -> ERROR. It names a path segment; "3962749" vs 3962749 is the same folder but different JSON,
  and that coercion drift is the exact species that broke C's atlas contract (licence/license).
- duplicate  -> ERROR. Two shops keyed to one atlas IS mis-stocking -- charter risk #3 forbids it by name.
- missing on a godverse cache -> WARN + fail-soft to tier 0.

RECORDED DEPARTURE (Fable's to overrule): ledger #2 says validateTownCache "requires" the field; I made
MISSING a warn. Three reasons. (1) The charter's determinism boundary is "world = seeded, frozen, gated;
stock = data tiers" -- an error inverts it, failing to load a TOWN because its STOCK identity is absent;
tier 0 must always boot. (2) Charter risk #3 names the remedy for identity gaps: "fail-soft to tier 0,
never mis-stocked" -- missing IS the soft case, duplicate is the hard one; they are different failures and
now get different arms. (3) Sequencing: newtown_godverse carries 0 ids today, it is G's to re-emit in wave
2 (their namespace, no census DB on m3), so a hard error would red the tree for C and E through all of wave
1 to say what the warn already says.
The requirement keeps its teeth where R23 taught us to put them -- the VACUOUS-GATE LAW: selfcheck FAILS on
partial keying (a half-keyed town silently mis-stocks the rest) and prints an explicit
"⊘ SKIP ... 0/18 shops carry godverseShopId. Subject absent ... binds the moment one id lands" at zero. It
names its subject, proves it touched it with a count, and cannot pass vacuously.

FILED FOR G BEFORE THEY START (measured in wave 1, so it costs them minutes not a wave): redhill_godverse
cannot contain Monster Robot from EITHER source. godverse_town.py takes roads from the OSM donor and shops
from the census, and the shop is in neither -- not in the census (G's R23 finding), and NOT in E's
redhill_real, which I checked the moment it landed: 36 shops, ZERO of type `record`, nothing matching
/monster|robot/. As written the adapter yields a Red Hill of census charity shops while the atlas keys to a
shop that isn't in the town -- F's #7b id-equality gate would fail at the end of the round. It must be
injected explicitly; the opaque-key design exists precisely to serve that case. Second trap on the same
path: redhill_godverse must still clear MIN_TOWN_SHOPS (6) from the census bbox or the cache is rejected
outright (hard error). E's donor is otherwise good: valid, no warns, 73.3 m median spacing.

HOUSEKEEPING: removed toowoomba's two stale pins -- E retired the cache in R23, and a golden for a town
that cannot load can never fire (the vacuous-gate law, one layer down). E's attempt also proved my R23
spacing metric is GAMEABLE: the re-bbox fixed the median (395.7 -> 89.4 m, "passes") while hub density fell
3/12 -> 2/12, the pack's worst. It is necessary, not sufficient -- D's hub test is the complement, and E
used both and refused to game it. Ballarat (255 m) is still flagged and still E's call; I pin what moves.

PINS NOT TAKEN YET, DELIBERATELY: redhill_real is untracked/in-flight (E still editing build_towns.py), and
redhill_godverse + the re-keyed newtown_godverse don't exist until wave 2. Pinning a moving file buys a
false green or a spurious red. The round's law says A pins these BATCHED -- that batch lands when E commits
and G's caches are in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:45:45 +10:00
docs Lane A round 24 (v5.0-alpha): the identity field — godverseShopId, published first (ledger #2) 2026-07-17 00:45:45 +10:00
pipeline Lane G R23 (v5.0-alpha): THE FIRST REAL CRATE — 120 real records from a real crate + the G3 draft 2026-07-16 23:27:28 +10:00
priorwork Fable (integrator): round-3 lane instructions + archive round-1 lane prompts 2026-07-14 14:30:36 +10:00
tools Lane F R22 (v4.0 — THE EPOCH CLOSE): the re-measure, the selector, the release tour, the tag 2026-07-16 22:32:32 +10:00
web Lane A round 24 (v5.0-alpha): the identity field — godverseShopId, published first (ledger #2) 2026-07-17 00:45:45 +10:00
.gitignore Fable: R14 kickoff rulings recorded (push done, flag stays opt-in, genres final) + gitignore local session files 2026-07-16 00:59:42 +10:00
B-progress.md Lane B (Streetscape): round-12 v3.0-alpha — venue frontage + posters + muffled-gig spill (?gigs=1) 2026-07-15 20:15:40 +10:00
C-progress.md Lane C R24: the contract fix — the runtime is the authority (ledger #1, the root cause) 2026-07-17 00:43:50 +10:00
D-progress.md Lane D round 12: gig band + crowd + patronage surge (?gigs=1) 2026-07-15 19:49:07 +10:00
E-progress.md Lane E R12 (v3.0-alpha gig layer): 5 instruments + 4 posters + 3 gig audio 2026-07-15 19:44:48 +10:00
F-progress.md Lane F R23 (v5.0-alpha): the alpha gate — HELD. The crate is real; it has no street. 2026-07-17 00:05:44 +10:00
G-progress.md Lane G R23 (v5.0-alpha): THE FIRST REAL CRATE — 120 real records from a real crate + the G3 draft 2026-07-16 23:27:28 +10:00
README.md Lane F R22 (v4.0 — THE EPOCH CLOSE): the re-measure, the selector, the release tour, the tag 2026-07-16 22:32:32 +10:00

PROCITY

A standalone, procedurally generated, fully walkable 90s-Australian shopping town — every building enterable (the Vuntra City trick), every interior seeded and themed: record stores, op shops, toy shops, book barns, video rental, pawnbrokers, milk bars, market stalls. Built on the proven house stack (three.js r175 vendored, plain JS, no build step) and the asset DNA of 90sDJsim + thriftgod. v1 is the system; the GODVERSE/BaseGod real-item content plugs in later.

Run

cd web && python3 -m http.server 8130
# http://localhost:8130            (game shell — Lane B)
# http://localhost:8130/map.html   (2D plan debugger — Lane A)

Read first

  • docs/CITY_SPEC.md — the treaty: schema, units, seeds, budgets, file ownership.
  • docs/RESEARCH.md — what the parent repos already solved, with exact paths to port from.

The lanes (parallel Opus 4.8 sessions)

Each lane is a self-contained prompt. To run one, start an Opus session in this repo and say: "Read docs/LANES/LANE_X_….md and execute it." AE are parallel-safe (disjoint files, standalone test pages); F integrates after they land.

lane mission owns
A — CITYGEN seed → CityPlan data (streets, blocks, lots, shops) + 2D map js/citygen, js/core/registry.js, map.html
B — STREETSCAPE chunk-streamed instanced 3D town + game shell index.html, js/world
C — INTERIORS every door opens: seeded themed shop interiors js/interiors, interior_test.html
D — CITIZENS rigged-near / impostor-far NPCs + shopkeepers js/citizens, citizens_test.html, models/
E — ASSETS audit ultra's library, normalize GLBs, skins, manifest pipeline/, assets/
F — INTEGRATION wire AE into one seed-to-town game + QA gates (after AE)

Already in the repo

  • web/vendor/ — three.js r175 + addons (vendored from 90sDJsim; no CDN at runtime).
  • web/assets/gen/ — 69 generated texture skins (25 shopfront facades, 10 skies, 10 grounds, 8 wallpapers, 16 interior surfaces) inherited from thriftgod + 90sDJsim, style-locked.
  • web/js/core/ — frozen shared modules: prng.js (seeded-everything law), loaders.js (fail-soft promise-cached GLB/texture + 3GOD depot), canvas.js (text planes).

House laws (short form — CITY_SPEC has the fine print)

Seeded everything, Math.random banned in gen code · the game must run with zero assets · instancing from day one · canvas text, no font files · characters = shared base rigs + one clip bank, canonical sources live outside this repo · 🟢 web-ok licensing only.

v3.1 — THE FLIP: the town shows its best by default

The Friday-night layer soaked through alpha, beta, and the v3.0 release — so v3.1 flips it on by default, and the mature v2 flags flip with it. Boot the plain URL and each town seeds 24 venues (pub, band room, RSL) with a full seeded week of gigs, town-wide posters, and a Friday night that reads: a 4-piece band (trio + a seated drummer) with real instruments, a mixed crowd, an outdoor queue, a per-venue cover charge, muffled gig spill + walla and applause, town-wide streetlamp pools, and identity continuity — the ped you followed in is in the crowd. Plus weather, the tram, and the parallax interior-window shader, all live by default.

The covenant moved, it didn't disappear. ?classic=1 boots the exact v2 town — byte-identical forever (synthetic golden 0x3fa36874, zero gig layer, zero fetch delta). That is the new frozen regression target; the default boot is deterministic to the gig golden 0xb1d48ea1. Asset-free (?noassets=1) composes with the default boot and stays silent-and-fine.

Flags — the four headliners are ON by default, each with a =0 opt-out; ?classic=1 is the master off:

flag default what
?classic=1 off the exact v2 boot — all four below off, byte-identical to the frozen baseline
?gigs=0 on the gig district — venues, week schedule, band/crowd/queue, cover, posters, night pass
?weather=0 · ?tram=0 · ?winmap=0 on seeded weather · the tram loop · the parallax interior-window shader
?roster=v1 · ?pop=N stream chunk-streamed citizen density is the baseline; ?roster=v1 restores the fixed roster
?dig=0 · ?stock=real dig on crate-riffle on record bins · real GODVERSE sleeves in the stock
?noassets=1 · ?plansrc=osm · ?mute=1 · ?dbg=1 off asset-free (zero network) · OSM plan source · silence · the window.DBG capture/soak harness
?plansrc=osm&town=<key> boot a real Australian town: fixtures melbourne/katoomba/silverton, or a real OSM cache bendigo_real/castlemaine_real/fremantle_real/katoomba_real/newtown_real (ODbL, web/assets/towns/SOURCES.md). REAL ROADS: a cache with roads[] (schema v2) builds the CityPlan street graph from the town's actual OSM streets; without roads[] a cache marches onto synthetic avenues. Or just pick a town from the HUD selector — no URL needed

v4.0 — THE REAL MAP: the towns are the product

23 real Australian towns — 1,210 real shops, every state and territory — on their actual streets. Not "inspired by": Fitzroy's shops sit on Fitzroy's roads, at their real positions, because the pipeline eats OpenStreetMap and marches it through the same CityPlan contract the procedural town uses. Pick one from the in-HUD selector and walk it. Everything built across v1v3 comes along unchanged — the venues and their seeded week of gigs, the 4-piece band with the seated drummer, the crowd that follows you into the pub, the interiors behind every door, weather, the tram — because they all hang off the plan contract and never had to know the geography got real.

The towns run from meccas (Fitzroy, 160 shops) to the thin tail (12), at real retail density: the secondhand heroes keep their footfall while the street around them gets ~4× busier — texture, never a takeover. Venues bias to the retail heart. The tram only runs where it fronts a real high street (elsewhere it was honestly "a highway bus", so it's fenced). Everything stays deterministic from the seed, silent under ?noassets=1, and ?classic=1 still boots the exact v2 town, byte-identical forever (0x3fa36874).

The tour: docs/shots/release_v4/contact.html — the Fremantle hero, the Fitzroy mecca, the thin tail (labelled honestly: the floor is shops-per-street-metre, and Toowoomba is below it), the synthetic control, and the ?classic=1 covenant made visible. Charter: docs/V4_REAL_MAP.md · data + licences (ODbL): web/assets/towns/SOURCES.md. | ?seed=N | 20261990 | town seed |

Release tour: docs/shots/release_v3_1/contact.html — the money shots on the default boot, plus a ?classic=1 before/after. QA: tools/qa.sh --strict (classic regression + default-boot gate + smokes + no-giants) --soak (the full-week soak); the seated drummer passes the no-giants gate at the seated stature band [0.9,1.5] m.