Lane F R17 (v3.2): real towns boot in the engine + the town-matrix scout gate (ledger #7)
F wired the last mile of the real-map scout and gated the whole matrix. - index.js barrel re-exports registerTownCache/validateTownCache; the shell fetches + registers assets/towns/<key>.json before generatePlanFor when the town isn't a checked-in fixture — so ?plansrc=osm&town=bendigo_real boots a REAL AU town in the engine (the gap: E's caches were on disk + in the node selfcheck, but the shell never loaded them → silent Melbourne fallback). Fail-soft (404 / invalid / ?noassets -> fixture fallback); opt-in, never on the default or ?classic path. - tools/qa/town_matrix.py (opt-in qa.sh --matrix): boot / determinism / budget / district / ?noassets across 10 towns (2 synthetic hero seeds + 3 osm fixtures + E's 5 real AU caches). ALL 5x10 GREEN — Castlemaine at the 6-shop floor still lands a 3-venue district. The existing CityPlan contract eats real Australian geography: the evidence John charters v4 = THE REAL MAP on. - docs: LANE_F_NOTES §17 (the town x gate table), README real-town row, F-progress R17. qa.sh --strict --soak GREEN 7/7. Verified: ?plansrc=osm&town=bendigo_real -> Bendigo, 9 shops, 3 venues, 0 errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
64ded4ea01
commit
e4a49b5bc8
@ -4,6 +4,22 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Round 17 (v3.2 THE TAIL + THE SCOUT) — F: the town matrix + the close
|
||||||
|
|
||||||
|
The tail drained (drummer lean + bench-sit, wind sway, backline verify, --verify fix) and E's real-map scout
|
||||||
|
landed (5 real AU town caches + A's cache contract + hardened plan_osm). **F closed it.**
|
||||||
|
|
||||||
|
- **Real towns boot in the engine** — the barrel re-exports `registerTownCache`; the shell fetches +
|
||||||
|
registers `assets/towns/<key>.json` before the plan generates (fail-soft; opt-in; never on default/classic).
|
||||||
|
`?plansrc=osm&town=bendigo_real` → **Bendigo, 9 shops, 3-venue district, 0 errors**.
|
||||||
|
- **Town-matrix smoke** (`tools/qa/town_matrix.py`, `qa.sh --matrix`) — **10 towns × 5 gates all green**:
|
||||||
|
2 synthetic + 3 fixtures + 5 real caches, each boot / deterministic / in-budget (69–216 draws) / a 3-venue
|
||||||
|
district / silent under `?noassets`. Castlemaine at the 6-shop floor still lands a district. **The existing
|
||||||
|
CityPlan contract eats real Australian geography** — the v4 = THE REAL MAP evidence.
|
||||||
|
- Close: `qa.sh --strict --soak` green, docs (LANE_F_NOTES §17, README), tag **`v3.2`**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Round 16 (v3.1 THE FLIP) — F bookends; the town shows its best by default
|
## Round 16 (v3.1 THE FLIP) — F bookends; the town shows its best by default
|
||||||
|
|
||||||
The gig layer soaked through three v3 stages, so v3.1 flips it (and the mature v2 flags) ON by default.
|
The gig layer soaked through three v3 stages, so v3.1 flips it (and the mature v2 flags) ON by default.
|
||||||
|
|||||||
@ -74,6 +74,7 @@ master off:
|
|||||||
| `?roster=v1` · `?pop=N` | stream | chunk-streamed citizen density is the baseline; `?roster=v1` restores the fixed roster |
|
| `?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 |
|
| `?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 |
|
| `?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** (v3.2 scout): fixtures `melbourne`/`katoomba`/`silverton`, or a real OSM cache `bendigo_real`/`castlemaine_real`/`fremantle_real`/`katoomba_real`/`newtown_real` (ODbL, `docs`/`web/assets/towns/SOURCES.md`) |
|
||||||
| `?seed=N` | `20261990` | town seed |
|
| `?seed=N` | `20261990` | town seed |
|
||||||
|
|
||||||
**Release tour:** [`docs/shots/release_v3_1/contact.html`](docs/shots/release_v3_1/) — the money shots on
|
**Release tour:** [`docs/shots/release_v3_1/contact.html`](docs/shots/release_v3_1/) — the money shots on
|
||||||
|
|||||||
@ -638,3 +638,40 @@ in the opening commit. Verified live: the seated drummer passes at **1.32 m stat
|
|||||||
actual shader is B's own `winmap` smoke.
|
actual shader is B's own `winmap` smoke.
|
||||||
- **`?classic` must zero the fetch delta**, so `sit.glb` is gated on `!CLASSIC` (not just on the fleet) — a
|
- **`?classic` must zero the fetch delta**, so `sit.glb` is gated on `!CLASSIC` (not just on the fleet) — a
|
||||||
raw sit fetch under classic would break the covenant even though the drummer never spawns there.
|
raw sit fetch under classic would break the covenant even though the drummer never spawns there.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Round 17 — v3.2 THE TAIL + THE SCOUT. F: the town matrix + the close (ledger #7).
|
||||||
|
|
||||||
|
The tail drained (D's drummer lean + bench-sit, B's wind sway, C's backline verify, E's `--verify` fix) and
|
||||||
|
the real-map scout ran (E's 5 real AU town caches + A's cache contract + hardened `plan_osm`). F wired the
|
||||||
|
last mile and gated the whole matrix.
|
||||||
|
|
||||||
|
### Real towns boot in the engine (the F wiring)
|
||||||
|
E's caches sat on disk (`web/assets/towns/*.json`) + registered in the node selfcheck, but **the shell never
|
||||||
|
loaded them** — `?plansrc=osm&town=bendigo_real` silently fell back to Melbourne. F closed the gap: the barrel
|
||||||
|
(`index.js`) re-exports `registerTownCache`/`validateTownCache`; the shell fetches `assets/towns/<TOWN>.json`
|
||||||
|
+ registers it before `generatePlanFor` when the town isn't a checked-in fixture. **Fail-soft** — 404 /
|
||||||
|
invalid / `?noassets` → plan_osm's fixture fallback; the scout data is opt-in, never on the default or
|
||||||
|
`?classic` path. Verified: `?plansrc=osm&town=bendigo_real` → **Bendigo, 9 shops, a 3-venue district, 0 errors**.
|
||||||
|
|
||||||
|
### The town matrix (`tools/qa/town_matrix.py`, opt-in `qa.sh --matrix`) — the v4 evidence
|
||||||
|
Boots every town on the DEFAULT boot and gates boot / determinism / budget / district / `?noassets`. **All
|
||||||
|
10 × 5 green:**
|
||||||
|
|
||||||
|
| town | shops | budget (draws/tris) | district |
|
||||||
|
|---|---|---|---|
|
||||||
|
| synthetic/20261990 · 1234 | 493 · 486 | 124d/57k · 147d/71k | 3v ✓ |
|
||||||
|
| osm/melbourne · katoomba · silverton | 95 · 19 · 12 | 216d · 70d · 70d | 3v ✓ |
|
||||||
|
| real/bendigo · castlemaine · fremantle · katoomba · newtown | 9 · 6 · 21 · 20 · 18 | 82 · 86 · 159 · 69 · 93 d | 3v ✓ |
|
||||||
|
|
||||||
|
The headline: **the existing CityPlan contract eats real Australian geography** — Castlemaine at the 6-shop
|
||||||
|
floor still lands a 3-venue district; every real town is deterministic, in-budget, and silent under
|
||||||
|
`?noassets`. No new game systems — exactly the bounded scout the round asked for. This table is the evidence
|
||||||
|
John charters **v4 = THE REAL MAP** on.
|
||||||
|
|
||||||
|
### Gotchas
|
||||||
|
- **A published cache + selfcheck ≠ a bootable town.** The node sweep proved the *plan* generates; the
|
||||||
|
*browser* boot needed the shell to fetch+register the cache. Classic integration seam — F owns it.
|
||||||
|
- **`?noassets` can't fetch a real cache**, so a real-town + noassets boot correctly falls back to a fixture
|
||||||
|
(still silent-and-fine). The matrix's noassets gate tests that fallback, not the real town — as it should.
|
||||||
|
|||||||
14
tools/qa.sh
14
tools/qa.sh
@ -126,6 +126,20 @@ if [ "$SOAK_RUN" = 1 ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Gate 7 (Lane F, round 17): town-matrix smoke — OPT-IN (--matrix), NON-STRICT ─
|
||||||
|
# The v4 real-map scout evidence: boot / determinism / budget / district / noassets across the full town
|
||||||
|
# matrix — synthetic hero seeds + the 3 osm fixtures + Lane E's 5 real AU town caches. One town × gate
|
||||||
|
# table. Slow (~20 boots) + diagnostic, so OPT-IN + warn-level. Wired per ROUND17 §Lane F (ledger #7).
|
||||||
|
MATRIX_RUN=0; for a in "$@"; do [ "$a" = "--matrix" ] && MATRIX_RUN=1; done
|
||||||
|
if [ "$MATRIX_RUN" = 1 ]; then
|
||||||
|
if [ -x tools/.venv/bin/python ] && tools/.venv/bin/python -c "import playwright" 2>/dev/null; then
|
||||||
|
warn_gate "town-matrix (synthetic + fixtures + real caches × 5 gates · Lane F R17)" \
|
||||||
|
tools/.venv/bin/python tools/qa/town_matrix.py
|
||||||
|
else
|
||||||
|
soft_skip "town-matrix" "Playwright venv absent"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Summary ──────────────────────────────────────────────────────────────────
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
||||||
hr
|
hr
|
||||||
printf '%sSUMMARY%s %s%d passed%s · %s%d failed%s · %s%d warn%s · %s%d skipped%s\n' \
|
printf '%sSUMMARY%s %s%d passed%s · %s%d failed%s · %s%d warn%s · %s%d skipped%s\n' \
|
||||||
|
|||||||
169
tools/qa/town_matrix.py
Normal file
169
tools/qa/town_matrix.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PROCITY Lane F — the town-matrix smoke (round 17, v3.2 · the real-map scout gate · ledger #7).
|
||||||
|
|
||||||
|
The gate side of the scout: run the core smokes across the FULL town matrix — synthetic hero seeds, the
|
||||||
|
three checked-in osm fixtures, and Lane E's real AU town caches — and emit one town × gate table. This is
|
||||||
|
the evidence John charters v4 (THE REAL MAP) on: proof the existing CityPlan contract eats real geography
|
||||||
|
through the default boot, with no new game systems.
|
||||||
|
|
||||||
|
Gates per town (all on the DEFAULT boot — gigs/weather/winmap/tram on, post-R16 flip):
|
||||||
|
• boot — the plan builds + the shell reaches window.PROCITY.plan, 0 console errors
|
||||||
|
• determ — two fresh in-page generations are byte-equal (the generator is deterministic)
|
||||||
|
• budget — the busiest default view (venue block at night) ≤ 300 draws / 200k tris
|
||||||
|
• district — the gig layer placed on this town: ≥1 venue, a week schedule, posters
|
||||||
|
• noassets — ?noassets=1 boots silent (0 errors, 0 network); real caches fall back to a fixture (correct)
|
||||||
|
|
||||||
|
Run: tools/.venv/bin/python tools/qa/town_matrix.py
|
||||||
|
Needs the Playwright venv + Lane B's ?dbg=1 hook + Lane F's real-cache shell wiring (R17).
|
||||||
|
"""
|
||||||
|
import sys, time, socket, subprocess, pathlib
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
|
||||||
|
PORT = 8130
|
||||||
|
HOST = f'http://127.0.0.1:{PORT}'
|
||||||
|
STREET_DRAWS_MAX, STREET_TRIS_MAX = 300, 200_000
|
||||||
|
|
||||||
|
# the matrix: (row-label, boot-query). Synthetic hero seeds + 3 fixtures + E's 5 real AU caches.
|
||||||
|
MATRIX = (
|
||||||
|
[(f'synthetic/{s}', f'seed={s}') for s in (20261990, 1234)]
|
||||||
|
+ [(f'osm/{t}', f'plansrc=osm&town={t}') for t in ('melbourne', 'katoomba', 'silverton')]
|
||||||
|
+ [(f'real/{t}', f'plansrc=osm&town={t}')
|
||||||
|
for t in ('bendigo_real', 'castlemaine_real', 'fremantle_real', 'katoomba_real', 'newtown_real')]
|
||||||
|
)
|
||||||
|
|
||||||
|
# default-boot gates 1-4, in one page. Determinism regenerates the plan twice in-page (the real cache is
|
||||||
|
# already registered by the shell) and compares — no second full boot needed.
|
||||||
|
DEFAULT_JS = r"""
|
||||||
|
async () => {
|
||||||
|
const P = window.PROCITY, D = window.DBG;
|
||||||
|
const plan = P.plan;
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const src = params.get('plansrc') === 'osm' ? 'osm' : 'synthetic';
|
||||||
|
const town = params.get('town') || undefined;
|
||||||
|
const cg = await import('./js/citygen/index.js');
|
||||||
|
const g1 = JSON.stringify(cg.generatePlanFor(plan.citySeed, src, { town, gigs: true }));
|
||||||
|
const g2 = JSON.stringify(cg.generatePlanFor(plan.citySeed, src, { town, gigs: true }));
|
||||||
|
const venues = (plan.shops || []).filter(s => s.venue).length;
|
||||||
|
D.shot('venue_night'); // busiest default view: the venue block at night
|
||||||
|
await new Promise(r => setTimeout(r, 500));
|
||||||
|
const i = D.info();
|
||||||
|
return { name: plan.name, shops: (plan.shops || []).length, venues,
|
||||||
|
gigs: (plan.gigs || []).length, posters: (plan.posters || []).length,
|
||||||
|
determ: g1 === g2, draws: i.drawCalls, tris: i.tris };
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def port_up(port):
|
||||||
|
with socket.socket() as s:
|
||||||
|
s.settimeout(0.4)
|
||||||
|
return s.connect_ex(('127.0.0.1', port)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_server():
|
||||||
|
if port_up(PORT):
|
||||||
|
return None
|
||||||
|
proc = subprocess.Popen(['python3', '-m', 'http.server', str(PORT)], cwd=str(ROOT / 'web'),
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
for _ in range(50):
|
||||||
|
if port_up(PORT): return proc
|
||||||
|
time.sleep(0.1)
|
||||||
|
proc.terminate(); sys.exit(f'could not start http.server on :{PORT}')
|
||||||
|
|
||||||
|
|
||||||
|
def run_town(new_page, query):
|
||||||
|
"""Returns a dict of gate → (ok, note) for one town."""
|
||||||
|
row = {}
|
||||||
|
# ── default boot: boot / determ / budget / district ──
|
||||||
|
pg = new_page()
|
||||||
|
errs = []
|
||||||
|
pg.on('console', lambda m: errs.append(m.text) if m.type == 'error' else None)
|
||||||
|
pg.on('pageerror', lambda e: errs.append(str(e)))
|
||||||
|
try:
|
||||||
|
pg.goto(f'{HOST}/index.html?{query}&dbg=1')
|
||||||
|
pg.wait_for_function('window.PROCITY && window.PROCITY.plan && window.DBG && window.DBG.ready', timeout=25000)
|
||||||
|
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
|
||||||
|
try:
|
||||||
|
pg.wait_for_function('() => !window.PROCITY.fleet || window.PROCITY.fleet.ready', timeout=12000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
d = pg.evaluate(DEFAULT_JS)
|
||||||
|
row['name'] = d['name']; row['shops'] = d['shops']
|
||||||
|
row['boot'] = (bool(d['name']) and len(errs) == 0, f"{d['shops']} shops, {len(errs)} err")
|
||||||
|
row['determ'] = (d['determ'], 'byte-equal' if d['determ'] else 'DIFF')
|
||||||
|
row['budget'] = (d['draws'] <= STREET_DRAWS_MAX and d['tris'] <= STREET_TRIS_MAX,
|
||||||
|
f"{d['draws']}d/{d['tris']//1000}k")
|
||||||
|
row['district'] = (d['venues'] >= 1 and d['gigs'] > 0,
|
||||||
|
f"{d['venues']}v/{d['gigs']}g/{d['posters']}p")
|
||||||
|
except Exception as e:
|
||||||
|
row.setdefault('name', '?'); row.setdefault('shops', 0)
|
||||||
|
for g in ('boot', 'determ', 'budget', 'district'):
|
||||||
|
row.setdefault(g, (False, str(e)[:24]))
|
||||||
|
finally:
|
||||||
|
pg.close()
|
||||||
|
# ── ?noassets=1: silent-and-fine (real caches correctly fall back to a fixture — no network) ──
|
||||||
|
pg = new_page()
|
||||||
|
reqs, errs2 = [], []
|
||||||
|
pg.on('request', lambda r: reqs.append(r.url) if ('/assets/towns/' in r.url or '/assets/audio/' in r.url or r.url.endswith('.glb')) else None)
|
||||||
|
pg.on('pageerror', lambda e: errs2.append(str(e)))
|
||||||
|
try:
|
||||||
|
pg.goto(f'{HOST}/index.html?{query}&noassets=1&dbg=1')
|
||||||
|
pg.wait_for_function('window.PROCITY && window.PROCITY.plan', timeout=20000)
|
||||||
|
has_plan = pg.evaluate("() => !!(window.PROCITY.plan && window.PROCITY.plan.shops.length)")
|
||||||
|
row['noassets'] = (has_plan and len(errs2) == 0 and len(reqs) == 0,
|
||||||
|
f"{len(reqs)} fetch/{len(errs2)} err")
|
||||||
|
except Exception as e:
|
||||||
|
row['noassets'] = (False, str(e)[:24])
|
||||||
|
finally:
|
||||||
|
pg.close()
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
except ImportError:
|
||||||
|
sys.exit('playwright not installed — tools/.venv/bin/python -m playwright install chromium')
|
||||||
|
|
||||||
|
print(f"\033[1mTOWN-MATRIX SMOKE\033[0m ({len(MATRIX)} towns × 5 gates · the v4 real-map scout evidence)")
|
||||||
|
srv = ensure_server()
|
||||||
|
GATES = ['boot', 'determ', 'budget', 'district', 'noassets']
|
||||||
|
rows = []
|
||||||
|
try:
|
||||||
|
with sync_playwright() as p:
|
||||||
|
b = p.chromium.launch()
|
||||||
|
def factory():
|
||||||
|
return b.new_page(viewport={'width': 1280, 'height': 720})
|
||||||
|
for label, query in MATRIX:
|
||||||
|
r = run_town(factory, query)
|
||||||
|
rows.append((label, r))
|
||||||
|
cells = ' '.join(f"{g}:{'✓' if r[g][0] else '✗'}" for g in GATES)
|
||||||
|
print(f" {label:22} {(r.get('name') or '?'):16} {cells}")
|
||||||
|
b.close()
|
||||||
|
finally:
|
||||||
|
if srv: srv.terminate()
|
||||||
|
|
||||||
|
# the table John reads
|
||||||
|
print(f"\n\033[1mTOWN × GATE\033[0m")
|
||||||
|
hdr = f"{'town':22} {'name':16} " + " ".join(f"{g:>9}" for g in GATES)
|
||||||
|
print(hdr); print('─' * len(hdr))
|
||||||
|
fails = 0
|
||||||
|
for label, r in rows:
|
||||||
|
line = f"{label:22} {(r.get('name') or '?'):16} "
|
||||||
|
for g in GATES:
|
||||||
|
ok, note = r[g]
|
||||||
|
fails += 0 if ok else 1
|
||||||
|
line += f"{('✓ ' if ok else '✗ ') + note:>9} "[:10] + " "
|
||||||
|
print(line)
|
||||||
|
# per-gate note detail
|
||||||
|
print("\nnotes:", "; ".join(f"{label}[{g}={r[g][1]}]" for label, r in rows for g in GATES if not r[g][0]) or "all gates green")
|
||||||
|
print()
|
||||||
|
if fails:
|
||||||
|
print(f"\033[31m● {fails} gate failure(s)\033[0m across {len(rows)} towns — the matrix is not clean.")
|
||||||
|
sys.exit(1)
|
||||||
|
print(f"\033[32m● MATRIX GREEN\033[0m — {len(rows)} towns (synthetic + fixtures + real caches) pass all 5 gates.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -111,6 +111,17 @@ const customBands = await loadCustomBands();
|
|||||||
let plan;
|
let plan;
|
||||||
try {
|
try {
|
||||||
const citygen = await import('./js/citygen/index.js');
|
const citygen = await import('./js/citygen/index.js');
|
||||||
|
// [Lane F R17 — real-town caches] a ?plansrc=osm&town=<key> whose key is NOT a checked-in fixture is one of
|
||||||
|
// Lane E's real AU town caches (web/assets/towns/<key>.json). Fetch + register it before the plan generates,
|
||||||
|
// so real towns boot in the engine (the v4 scout). Fail-soft: 404 / invalid cache / ?noassets → plan_osm
|
||||||
|
// falls back to the default fixture. The scout data is opt-in — never on the default or ?classic path.
|
||||||
|
if (PLANSRC === 'osm' && TOWN && !NOASSETS && citygen.registerTownCache
|
||||||
|
&& !(citygen.osmTownKeys && citygen.osmTownKeys().includes(TOWN))) {
|
||||||
|
try {
|
||||||
|
const r = await fetch(`assets/towns/${TOWN}.json`, { cache: 'force-cache' });
|
||||||
|
if (r.ok) { citygen.registerTownCache(TOWN, await r.json()); console.log(`[procity] real town cache registered: ${TOWN}`); }
|
||||||
|
} catch (e) { console.warn(`[procity] town cache '${TOWN}' not loaded (${e && e.message || e}) — falling back to a fixture`); }
|
||||||
|
}
|
||||||
const gen = citygen.generatePlanFor
|
const gen = citygen.generatePlanFor
|
||||||
? (s) => citygen.generatePlanFor(s, PLANSRC, { town: TOWN, gigs: GIGS_ON, customBands }) // A's selector (synthetic | osm[+town]) + the v3 gig layer
|
? (s) => citygen.generatePlanFor(s, PLANSRC, { town: TOWN, gigs: GIGS_ON, customBands }) // A's selector (synthetic | osm[+town]) + the v3 gig layer
|
||||||
: (citygen.generatePlan || citygen.default); // pre-R6 fallback (synthetic only)
|
: (citygen.generatePlan || citygen.default); // pre-R6 fallback (synthetic only)
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
export { generatePlan, chunkIndex, chunkKey, CHUNK, lotCorners, obbOverlap, isOpen } from './plan.js';
|
export { generatePlan, chunkIndex, chunkKey, CHUNK, lotCorners, obbOverlap, isOpen } from './plan.js';
|
||||||
export { shopName, townName, bandName } from './names.js';
|
export { shopName, townName, bandName } from './names.js';
|
||||||
export { generatePlanOSM, osmTownKeys } from './plan_osm.js';
|
export { generatePlanOSM, osmTownKeys, registerTownCache, validateTownCache, MIN_TOWN_SHOPS } from './plan_osm.js';
|
||||||
export { withGigs, gigKeyFor, POSTER_CLEAR } from './gigs.js';
|
export { withGigs, gigKeyFor, POSTER_CLEAR } from './gigs.js';
|
||||||
// The street-corridor law + venue vocabulary, re-exported so consumers get the whole Lane A contract
|
// The street-corridor law + venue vocabulary, re-exported so consumers get the whole Lane A contract
|
||||||
// from one import (ROUND13). `roadWidth`/`vergeBand`/`poleOffset` are the road-vs-verge split of
|
// from one import (ROUND13). `roadWidth`/`vergeBand`/`poleOffset` are the road-vs-verge split of
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user