# LANE A — CITYGEN (the plan generator) > **Prompt for Opus 4.8.** You are working in `/Users/jing/Documents/PROCITY`. Read > `docs/CITY_SPEC.md` (the contract) and `docs/RESEARCH.md` (what's already solved) before > writing any code. You own `web/js/citygen/*`, `web/js/core/registry.js`, and `web/map.html`. > Do not touch any other lane's files (ownership table in CITY_SPEC). No npm, no build step, > no TypeScript — plain ES modules, three.js style of the house repos. All randomness through > `web/js/core/prng.js`. Commit in small, described steps on `main`. ## Mission Pure-data town generator: `generatePlan(citySeed) → CityPlan` per the schema in CITY_SPEC, in under 100ms, fully deterministic, JSON-serializable, **no THREE import anywhere in this lane except the debug map page**. Plus the shop-type registry and a 2D debug map viewer. ## Design brief (the town we want) Not a Manhattan grid, not noise-soup. A **1km² Australian country-town-meets-inner-suburb core**: 1. **Main street spine** — one gently bent high street running roughly N–S through the origin, 28m corridor (road 10m + verandah'd footpaths). Continuous retail frontage: narrow lots (6–9m frontage, 12–20m deep), zero side setbacks, 1–2 storeys, occasional 3-storey corner anchor. This is where record/toy/book/video shops cluster. 2. **Cross streets** every 80–140m; the two central ones are "second high streets" (side kind), the rest taper into `backstreets`. 3. **One arcade** — a covered pedestrian lane (`kind:'arcade'`, width 5) cutting through a mid-spine block, lined with tiny lots (3–5m frontage) both sides. Prime record-store turf. 4. **Market square** district — an open block near the origin: `stall` lots in rows, the town's `dept` anchor on one edge. 5. **Warehouse fringe** (N or S end, seeded): bigger lots, corrugated/besser skins, sparse — future flea-market/venue territory. 6. **Residential collar** — the outer ring: `house` lots with front yards, and exactly 2–4 corner-lot `milkbar`s embedded in it (the classic Australian corner shop). 7. Laneways (`kind:'lane'`, width 4) behind main-street blocks for service/back-door flavour. ## Algorithm sketch (suggested, not sacred) - Spine as a polyline: 5–7 nodes, x-jitter ±30m. Offset cross streets perpendicular-ish with ±10° jitter. Snap intersections into shared nodes; build the half-edge/block extraction by walking the planar graph (blocks = faces). Keep it simple — the graph is small (<200 edges); an O(n²) face walk is fine. - Subdivide each block's street-facing sides into lots by frontage bands per district kind. Every lot records `frontEdge` (street edge id) and `ry` (facing rotation) so Lane B knows which way the facade points. Interior leftover = `yard`/`infill`. - Assign shop types by district weights (registry-driven), with clustering bonuses (a record shop next to an opshop next to a bookshop is the vibe — use a per-block shuffle that favours same-cluster neighbours), then assign `facadeSkin` from the type's pool (seeded), `storeys`, `hours` (seeded around 9–5, milkbars longer, one weirdo open till late). ## Shop names & signs `web/js/citygen/names.js`: seeded generator with per-type patterns and 90s-AU flavour (e.g. record: "{Word} Records", "Rotation", "{Suburb} Sound Exchange"; opshop: "St {Name}'s Opportunity Shop", "Vinnies-alike parodies"; milkbar: "{Family} Milk Bar"). 40+ patterns, plus a `sign` short form (what fits on the signboard). Keep it wordlist-based and deterministic — **optionally** ship `pipeline/gen_names.py` that asks m3ultra Ollama (`http://100.89.131.57:11434`, model `qwen3:32b`) to expand the wordlists into `web/js/citygen/wordlists.js` — generation uses only the checked-in wordlists at runtime (no network calls in the game). ## Registry (`web/js/core/registry.js`) Export `SHOP_TYPES` exactly covering the table in CITY_SPEC: per type — facade skin pool (filenames that exist in `web/assets/gen/`), sign style hints, interior archetype id, fittings mix, district weights, hours profile. This file is the shared vocabulary — every other lane imports it, so land it early and keep it flat data. ## Debug map (`web/map.html`) Standalone page, Canvas 2D (steal the thriftgod REFIDEX vibe): draws streets (width-scaled), blocks, lots coloured by use, shop dots coloured by type with name labels on hover, chunk grid overlay, seed input box + "regen" + "export JSON" buttons. This page is how humans (and Lane F) eyeball a seed. Make it pleasant — it becomes the in-game map later. ## Deliverables 1. `web/js/citygen/plan.js` — `generatePlan(citySeed)`, `chunkIndex(plan)`. 2. `web/js/citygen/names.js` (+ `wordlists.js`). 3. `web/js/core/registry.js`. 4. `web/map.html` debug viewer. 5. `web/js/citygen/selfcheck.js` — run with `node web/js/citygen/selfcheck.js` (plain node, no deps): asserts determinism (two runs, deep-equal), plan under 100ms, every shop has a lot, every lot has a valid frontEdge, no overlapping lots within a block, every facadeSkin file exists (fs check against `web/assets/gen/`), chunkIndex covers every lot, JSON round-trip. ## Acceptance - `node web/js/citygen/selfcheck.js` prints all-green. - `web/map.html` renders 5 different seeds that all read as *towns* (spine visible, arcade present, market square, milkbars in the collar) — screenshot each into `docs/shots/laneA/`. - Zero `Math.random`, zero THREE imports in citygen (grep-clean).