Spaces:
Running on Zero
Running on Zero
| # NEMOCITY — Architecture Contract (v1, June 12 2026, ~7pm) | |
| > **This file is the single source of truth for the parallel build.** Every builder agent reads | |
| > this FIRST and writes ONLY the files it owns (see File Ownership). When in doubt, follow the | |
| > schemas here exactly — the integrator fixes drift, but don't create it. | |
| ## One-liner | |
| Ask the city for a building — a 9B Nemotron city hall reads your petition, the deterministic | |
| engine zones it, and you (and every other visitor on Earth) watch it rise, floor by floor, in ONE | |
| shared persistent miniature city. Cars commute, rush hour jams the bridge, and the AI City | |
| Engineer reads real traffic telemetry and fixes it while you watch. | |
| - **Name:** NEMOCITY — "the tiny city the internet builds." (nano = Nemotron Nano + the | |
| hackathon's small-model thesis; -polis = city.) | |
| - **Tagline:** "Ask for a building and watch it rise — one tiny persistent city the whole | |
| internet builds, with a 9B Nemotron running city hall." | |
| - Track: 🍄 Thousand Token Wood. Hackathon: Build Small (Gradio + HF). Deadline June 15 morning. | |
| - Awards targeted: NVIDIA team-judged RTX 5080 (Nemotron load-bearing — the model's raw JSON | |
| plan is shown in the UI on every action), TTW podium, Community Choice (shared persistent | |
| city), Best Demo (timelapse + FPV walk footage), Judges' Wildcard, badges: offbrand, sharing, | |
| fieldnotes (+ offgrid/llama stretch). | |
| ## Non-negotiable design rules | |
| 1. **The "her" pattern** (org-celebrated judge taste): the deterministic engine owns ALL city | |
| state, placement, geometry, and traffic facts. The LLM only interprets petitions into a tiny | |
| JSON (no coordinates, EVER) and narrates. The engine clamps/repairs everything; a visitor | |
| action ALWAYS yields visible construction. | |
| 2. **One petition at a time** — single serialized queue, visitors see their position. | |
| 3. **The landing page must be gorgeous with ZERO GPU.** The whole city renders client-side from | |
| the event JSON; cars/citizens/day-night simulate deterministically in the browser. Judges | |
| evaluate while GPUs sleep. | |
| 4. **Determinism end-to-end.** All motion is a pure function of (event list, simTime). NO | |
| `Math.random()`, NO `Date.now()` inside sim/render logic (the shared clock anchor in main.js | |
| is the single allowed wall-clock read). NO `position += v*dt` accumulation — always evaluate | |
| `position(t)` absolutely (the godseed life.js idiom). This makes replay, timelapse, shared | |
| sync, and headless screenshots free. | |
| 5. **ONE `@spaces.GPU` call per user action** (one-shot JSON generation, model resident on cuda, | |
| duration=240). The multi-call loop crashes ZeroGPU — June 12 lesson, never relitigate. | |
| 6. **Moderation before execution** (charset/length + folded wordlist; LLM judge OFF by default). | |
| LLM-proposed building NAMES also pass the wordlist. One slur on a storefront during judging | |
| week ends every award. | |
| 7. **≤32B params total, ONE model:** `nvidia/NVIDIA-Nemotron-Nano-9B-v2`. | |
| 8. **Copy/naming:** never use "wish", "god", "remember/remembers", "Book of ___" in USER-FACING | |
| copy (rival differentiation + GODSEED separation). The user-facing words: **petition**, | |
| **city hall**, **City Engineer**, **ledger**. (Wire format & Python internals KEEP godseed's | |
| `wish_id`/`wish_*` names verbatim — zero-risk code reuse; UI copy is the only rename layer.) | |
| 9. Frame: a FLAT city grid (true SimCity feel). NOT a planet, NOT an island. Plus a first-person | |
| walk mode ("Walk the streets") — verified unclaimed across all 385 rival submissions. | |
| ## Repo layout & FILE OWNERSHIP (do not touch files you don't own) | |
| ``` | |
| nemocity/ | |
| ARCHITECTURE.md (this file — read-only for everyone) | |
| app.py ← AGENT-SERVER (adapt from godseed; already copied here) | |
| requirements.txt ← AGENT-SERVER | |
| README.md ← AGENT-SERVER (HF Space card w/ full YAML tags — see Deploy) | |
| server/ ← AGENT-SERVER (sse.py, persistence.py, schemas.py, ratelimit.py — copied, adapt envs/fields) | |
| engine/ ← AGENT-ENGINE | |
| constants.py (canonical: grid, river, building table, road classes, traffic constants, palette-relevant hues) | |
| genesis.py (the exact genesis event list — see Genesis) | |
| world.py (copied from godseed — keep the Feature/load/apply/state_dict machinery; adapt _observation + validation hook) | |
| tools.py (NEW: kind-synonym map, clamp tables, event validation — engine-side "tool" specs for the 4 event types) | |
| city.py (derived state from events: occupancy grid, road graph + street names, population, growth_radius) | |
| placement.py (the never-fails placement scorer + connector BFS) | |
| traffic.py (static assignment: trip gen, A*, demand map, Traffic Index, fix candidates + predicted index) | |
| summary.py (≤600-char city summary for the LLM prompt) | |
| moderation.py (copied — adapt env names GODSEED_→CITY_) | |
| queue_worker.py (copied — adapt: one-shot planner drive, fix-kind jobs, plan SSE event) | |
| mind/ ← AGENT-MIND | |
| prompts.py (build + fix one-shot prompts; persona; tool docs from engine tables) | |
| backends.py (copied — adapt: env names, ONESHOT only, model id, /no_think + <think> strip stays) | |
| validate.py (lenient parse for the BUILD and FIX JSON schemas; balanced-brace extraction + salvage) | |
| planner.py (one-shot grant(): prompt → generate → parse → clamp → engine acts; fix(): stats → generate → choice) | |
| mock_scripts.py (keyword→BUILD-JSON scripts exercising every kind; fix script; drives local dev + demos) | |
| moderation_judge.py (copied; off by default via CITY_JUDGE) | |
| web/ | |
| index.html, style.css ← AGENT-UI (HUD per Visual Bible; element ids are the contract) | |
| main.js ← AGENT-UI (copied — adapt SSE applier, petition form, HUD, mode buttons, mock boot, window.__app harness) | |
| mock/world.json ← generated by tools/gen_web.py (AGENT-ENGINE writes the generator; integrator runs it) | |
| mock/feed.js ← AGENT-UI (scripted petition feed for ?mock=1 demos) | |
| lib/ (vendored three r160 + addons + PointerLockControls — already in place; read-only) | |
| city/ | |
| constants.js ← generated by tools/gen_web.py from engine/constants.py (AGENT-RFOUND hand-seeds it from THIS contract; gen overwrites later — values must match this doc) | |
| rng.js (copied verbatim — mulberry32; read-only) | |
| util.js ← AGENT-RFOUND (cellToWorld/worldToCell/key/clamp/lerp/eases/cityCenter) | |
| scene.js ← AGENT-RFOUND (copied — adapt: fog, camera start, tilt-shift+grade passes, MSAA target, rig-swap hook, daylight wiring) | |
| ground.js ← AGENT-RFOUND (ground chunks, river water, vertex-color lawn, contact-shadow quads API, occupancy mirror + isWalkable) | |
| world.js ← AGENT-RFOUND skeleton; INTEGRATOR owns final wiring (event dispatch → subsystems) | |
| replay.js (copied verbatim; read-only until integrate) | |
| buildings.js ← AGENT-RKIT (the 19-kind procedural kit, global window InstancedMesh, hue/variant jitter) | |
| construction.js ← AGENT-RKIT (dirt lot → scaffold → crane → floor-by-floor rise → finish; progress = f(ev.t, simTime)) | |
| roads.js ← AGENT-RTRAFFIC (road/bridge/sidewalk/crosswalk meshes from road cells; canvas atlas; lamp posts) | |
| traffic.js ← AGENT-RTRAFFIC (road graph, A* + path cache, trip gen, 10Hz car CA, congestion EMA, Traffic Index, heatmap overlay) | |
| vehicles.js ← AGENT-RTRAFFIC (instanced car kit rendering of traffic.js car states; head/taillights; blob shadows) | |
| citizens.js ← AGENT-RTRAFFIC (instanced pedestrians on sidewalk loops) | |
| sky.js ← AGENT-RCAM (sun arc, hemisphere, fog colors, gradient sky dome, stars; exported pure daylight(simTime)) | |
| camera.js ← AGENT-RCAM (orbit/pan/zoom rig, gaze(point,{hold}), landing choreography, bounds) | |
| fpv.js ← AGENT-RCAM (pointer-lock walk: WASD+shift, occupancy collision w/ axis slide, enter/exit) | |
| tools/ | |
| shot.mjs (copied — AGENT-UI adapts selectors/harness; the screenshot-and-LOOK loop is mandatory) | |
| gen_web.py ← AGENT-ENGINE (emits web/city/constants.js + web/mock/world.json from engine constants/genesis) | |
| tests/ ← test_engine.py + test_traffic.py (ENGINE), test_mind.py (MIND), test_api.py (SERVER) | |
| traces/ (runtime output — wishes.jsonl; gitkeep only) | |
| ``` | |
| Build waves: **Wave 1 (parallel):** ENGINE, MIND, SERVER, RFOUND, UI. **Wave 2 (parallel):** | |
| RKIT, RTRAFFIC, RCAM. **Wave 3:** INTEGRATOR. **Wave 4:** verify (pytest + headless screenshots). | |
| ## The grid | |
| - 64×64 cells; cell coords `(cx, cz)` each in `[-32, 31]`. `CELL = 4` world units (= 4 m; | |
| 1 unit = 1 m — human scale, FPV-ready). City plane = 256×256 u at y=0. | |
| - World position of cell center: `x = cx*4 + 2`, `z = cz*4 + 2`. +X = east, +Z = south. | |
| - A cell holds at most ONE of: road | water | building-footprint | park | empty. | |
| - Buildings occupy `w×d` rectangular footprints, anchor = top-left (min cx, min cz) cell. | |
| - **The river** (load-bearing: guarantees the traffic bottleneck + a beauty anchor). Water cell | |
| columns per row, formula (identical in `engine/constants.py` and `web/city/constants.js`): | |
| ``` | |
| riverCols(cz) = cz <= -5 ? [7, 8] : cz <= 3 ? [8, 9] : [9, 10] | |
| ``` | |
| Water cells are unbuildable and uncrossable except on bridge cells (road cells over water). | |
| - `growth_radius` = max Chebyshev distance of any developed (non-water) cell from (0,0), min 6. | |
| Placement penalizes beyond `growth_radius + 2` → the city accretes outward, ring by ring. | |
| ## Shared sim clock (every visitor sees the same city at the same moment) | |
| - `CITY_EPOCH_S = 1781222400` (June 12 2026 00:00 UTC; constant in engine + web constants). | |
| - `simTime = serverNow - CITY_EPOCH_S` (seconds). main.js estimates `serverNow` once from | |
| `server_now` in `/api/state` (and SSE hello), then free-runs on the fixed-step clock, slewing | |
| ±2% toward the server clock when a heartbeat shows >1.5 s drift. Never snap. | |
| - ONE in-game day = `DAY_S = 240` seconds. `dayHours = (simTime % 240) / 240 * 24`. | |
| - Rush hours: bell curves at dayHours 8 and 18 (σ≈1.2h) — judges see a rush within ~2 min. | |
| - Day/night: `daylight(simTime)` ∈ [0,1], pure function in sky.js — smooth sunrise centered | |
| dayHours 5.5, sunset centered 19.5 (transitions ~1.5h wide). Night is short and gorgeous. | |
| - Construction progress (any client, incl. late joiners): | |
| `progress = clamp((CITY_EPOCH_S + simTime - ev.t) / DURATION(kind), 0, 1)`. | |
| - `tick(dt)`/`seek(t)` fixed-step core (already in scene.js) MUST keep working: timelapse, | |
| replay, and shot.mjs depend on it. | |
| ## Building table (CANONICAL — engine/constants.py is the source; gen_web.py mirrors to JS) | |
| `kind: {w×d, floors min–max, residents, jobs, attract, duration_s}` | |
| | kind | w×d | floors | residents | jobs | attract | duration | | |
| |---|---|---|---|---|---|---| | |
| | house | 1×1 | 1–2 | 4 | 0 | 0 | 20 | | |
| | townhouse | 1×1 | 2–3 | 6 | 0 | 0 | 20 | | |
| | apartments | 2×2 | 4–7 | 16 | 0 | 0 | 45 | | |
| | cafe | 1×1 | 1 | 0 | 3 | 5 | 20 | | |
| | shop | 1×1 | 1–2 | 0 | 4 | 4 | 20 | | |
| | market | 1×2 | 1 | 0 | 8 | 6 | 30 | | |
| | bank | 2×1 | 2–4 | 0 | 12 | 2 | 30 | | |
| | office | 1×1 | 3–8 | 0 | 40 | 1 | 30 | | |
| | tower | 2×2 | 8–16 | 0 | 60 | 2 | 45 | | |
| | school | 2×2 | 1–2 | 0 | 10 | 3 | 30 | | |
| | hospital | 3×3 | 3–6 | 0 | 25 | 2 | 45 | | |
| | fire_station | 2×1 | 2 | 0 | 8 | 1 | 30 | | |
| | warehouse | 2×2 | 1 | 0 | 10 | 0 | 30 | | |
| | factory | 3×2 | 1–2 | 0 | 18 | 0 | 45 | | |
| | park | 2×2 | 0 | 0 | 0 | 8 | 20 | | |
| | plaza | 1×1 | 0 | 0 | 0 | 6 | 20 | | |
| | stadium | 4×4 | 1 | 0 | 15 | 9 | 45 | | |
| | church | 2×1 | 1 | 0 | 2 | 3 | 30 | | |
| | town_hall | 2×2 | 2 | 0 | 6 | 4 | 45 | | |
| - Mixed-use feel comes from variety + placement affinities, not more kinds. | |
| - **Synonym map** (engine/tools.py — the model can never pick a kind we lack): skyscraper/ | |
| highrise→tower, apartment/apartment building/condo→apartments, coffee shop/diner/restaurant/ | |
| bakery→cafe, store/boutique/pharmacy→shop, mall/grocery/supermarket→market, police station/ | |
| police→fire_station, library/museum/city hall→town_hall, temple/mosque/chapel→church, | |
| garden/playground→park, fountain/square→plaza, arena/stadium→stadium, clinic→hospital, | |
| gym→shop, hotel→apartments, factory/plant→factory, anything unknown→**house**. | |
| - `population = Σ residents × build_progress` (a finishing tower visibly ticks the HUD up). | |
| - Citizens (pedestrians): `clamp(ceil(population/5), 6, 80)`. | |
| - Demand-driven infill: after a granted petition, if `jobs > housing_capacity*1.15`, engine | |
| appends ONE bonus house/apartments event (same wish_id, normal scorer, near the new jobs), | |
| note blurb "New families are moving in." Max 1 infill per petition. | |
| ## Roads & the road graph | |
| - Roads are CELLS. Classes: `street` {capacity 2, speed 1.0 cells/s, 1 dash lane marking}, | |
| `avenue` {capacity 6, speed 1.6, wider look, double yellow center}. Road cells over water | |
| render as BRIDGES (raised deck + railings; same graph semantics). | |
| - Graph (derived IDENTICALLY in Python `engine/city.py` and JS `traffic.js`): node = road cell; | |
| edge between 4-adjacent road cells. Intersection = road cell with ≥3 road neighbors. | |
| - **Street names:** each maximal straight run of road cells laid in one event gets a name from a | |
| curated list indexed by `crc32(event_id) % len(list)` ("Main St" and "1st Ave" and "Old Bridge" | |
| fixed in genesis). Names power LLM stats ("Old Bridge carries 3.1× its capacity") + HUD. | |
| - **Engine-routed roads only — the LLM never outputs geometry:** | |
| 1. Placing a building with no road frontage auto-routes a CONNECTOR: BFS over empty cells from | |
| footprint perimeter to nearest road cell (max length 8), Manhattan path, emitted as its own | |
| `lay_road` event in the same wish. | |
| 2. Traffic fixes lay engine-routed bypass roads (BFS avoiding jammed cells; water crossable at | |
| 4× cost — fixes may deliberately build a second bridge). | |
| - `road_version` = count of road-mutating events; all path caches key on it. | |
| ## Traffic model (client-side presentation; Python mirror for facts) | |
| - Fixed **10 Hz logic tick** (accumulator inside the existing 60 Hz fixed-step; render | |
| interpolates). RNG = mulberry32 from event seeds only. | |
| - **Trip generation** (static OD, gravity; recompute only when buildings change): each completed | |
| residential building emits `residents` commuters; commuter `i` picks a destination via seeded | |
| roulette over job/attract buildings, `weight = (jobs + 2*attract) / (1 + manhattanDist^1.5)`, | |
| `seed = building.seed ^ i`. | |
| - **Cars:** visible cars `N = clamp(round(population * 0.8 * rushFactor(simTime)), 8, 120)`, | |
| `rushFactor = 0.35 + 0.65 * (bell(dayH,8,1.2) + bell(dayH,18,1.2))` (bell = gaussian, capped | |
| at 1). Each car = one commuter loop home→work (dwell 10–20 s seeded)→home, phase-offset seeded. | |
| - **Pathfinding:** A* over road cells, `cost(cell) = (1/speed(class)) * (1 + 2*demandRatio(cell))` | |
| — congestion-aware so fixes visibly reroute. Cache per (origin, dest, road_version); on bump, | |
| re-solve incrementally ≤20 paths/frame. | |
| - **Movement — queueing cellular automaton** (no physics): car = scalar progress along its path; | |
| per tick `speed = base * clamp(capacity / max(occ(nextCell), 1), 0.25, 1)`; if | |
| `occ(nextCell) ≥ 2*capacity` the car HOLDS. occ = integer car count per road cell. Cars | |
| visibly slow, bunch, queue back from the bridge. Intersection yield = deterministic priority | |
| (lower car index). U-turn at dead ends. | |
| - **Congestion:** per road cell `cong = EMA(occ/capacity, τ=5 s)`. **Traffic Index** = | |
| `round(100 * mean(top-15 cong cells))`, color-coded on the HUD live. | |
| - **Heatmap overlay** (toggle): one InstancedMesh of road quads, green < 0.5 / yellow < 1.0 / | |
| red ≥ 1.0, instanceColor updated at 2 Hz. Jammed segments ALSO get an in-world additive red | |
| underglow strip (#E84F3D) — utility visible from orbit, not just HUD. | |
| - **Python mirror** (`engine/traffic.py`): same trip gen + A* + demand map from the same event | |
| list/constants (static assignment, no CA). This feeds the LLM stats + fix candidates, so the | |
| ENGINE owns the facts; live car queueing is presentation and may drift a car or two, harmless. | |
| - **ACCEPTANCE (engine agent must verify, tune capacities/CAR rate until true):** genesis city | |
| + ~3 extra houses west jams the Old Bridge (cong ≥ 1.0 red) at rush hour in mock mode. | |
| ## The traffic-fix mechanic (V0-CRITICAL — the prize-winning beat; never let it slip) | |
| 1. Client shows live Traffic Index; when any cell holds cong ≥ 1.0 for 10 s → pulsing chip | |
| "Traffic alert: Old Bridge" + button **[Ask the City Engineer]**. | |
| 2. `POST /api/fix` → engine gate: if Python static assignment max demandRatio < 0.8 → 409 | |
| "Traffic is flowing smoothly." Else enqueue a fix job (global cooldown 120 s, 2/hr/client). | |
| 3. Engine computes stats snapshot: top-5 cells by demand/capacity WITH street names, worst | |
| intersection, traffic_index, and 2–4 pre-validated CANDIDATE FIXES, e.g. | |
| `{id:"F1", action:"new_road", desc:"second bridge at Oak St, 7 cells", predicted_index:41}` | |
| (predicted by re-running static assignment with the candidate applied). Actions v0: | |
| `new_road` (bypass/bridge) and `upgrade_avenue` (repaint a named street's cells to avenue). | |
| 4. **ONE** @spaces.GPU Nemotron call: input {stats, candidates} → output | |
| `{"diagnosis":"≤160 chars citing the real numbers","choice":"F1","blurb":"≤120"}`. | |
| Invalid choice/parse failure → engine applies best predicted candidate + canned diagnosis. | |
| 5. Engine appends ONE `apply_fix` event (exact cells + diagnosis + metrics_before/predicted); | |
| SSE `world_delta`. Every client: road_version bumps, paths re-solve, the new road paints in | |
| over 4 s, queued cars peel onto it, red drains green over ~20 s, HUD animates "78 → 41". | |
| ## Placement algorithm (engine/placement.py — deterministic, NEVER fails, Python-only) | |
| Input: kind (post-synonym), w×d, floors (clamped), anchor from the "near" hint, resolved fuzzily: | |
| named building → its centroid; street name → midpoint; "center"/"downtown" → (0,0); "river" → | |
| nearest water-adjacent developed cell; null/unresolvable → centroid of all buildings. | |
| 1. Candidates = empty cells within Chebyshev radius R of anchor; R starts 6, +4 until ≥1 valid. | |
| 2. VALID = footprint fits on empty non-water cells AND (perimeter touches a road OR connector | |
| BFS ≤8 exists). | |
| 3. SCORE: `+30*(1 - dist_to_anchor/R)` `+20*roadFrontage` `+3/developed-8-neighbor (cap +18)` | |
| + affinities: cafe/shop +12 if house/apartments ≤3 cells; office/bank/tower | |
| `+10*(1 - dist_to_center/12)` (downtown skyline emerges); house/townhouse/apartments +8 if | |
| cafe/shop/park ≤4, −6 if factory/warehouse ≤3; factory/warehouse −10 if residential ≤3, +8 | |
| if industrial ≤4 (an industrial quarter self-organizes); park −25 if another park ≤6; | |
| `−8*max(0, chebyshev_from_center − (growth_radius+2))` (ring growth). | |
| 4. floors: office/tower `clamp(base + round(4*(1 − dist_to_center/10)), range)` — taller downtown. | |
| 5. Tie-break: seeded jitter `U(0,1)` from `crc32(wish_id:candidate_index)`. | |
| 6. Emit `place_building` (exact cells FINAL) + optional connector `lay_road`. Re-derive occupancy | |
| after EACH building within a multi-building petition (siblings cluster, never overlap). | |
| ## Event schema (append-only; byte-compatible with godseed Feature: id, wish_id, tool, args, seed, t) | |
| `seed = crc32(f"{wish_id}:{call_index}") & 0x7fffffff`. City-at-T = pure fn of (events t≤T, T). | |
| ```json | |
| {"id":"e_000017","wish_id":"w_000005","tool":"place_building", | |
| "args":{"kind":"cafe","name":"Cafe Luna","cx":3,"cz":-2,"w":1,"d":1,"floors":1,"hue":35,"variant":2}, | |
| "seed":1830293847,"t":1781230000.0} | |
| {"id":"e_000018","wish_id":"w_000005","tool":"lay_road", | |
| "args":{"cells":[[3,-1],[4,-1],[5,-1]],"klass":"street","name":"Olive St"},"seed":482919,"t":1781230000.5} | |
| {"id":"e_000031","wish_id":"w_000009","tool":"apply_fix", | |
| "args":{"action":"new_road","cells":[[5,-6],[6,-6],[7,-6],[8,-6],[9,-6]],"klass":"avenue", | |
| "name":"Oak St Bridge","diagnosis":"Old Bridge carries 3.1x capacity at rush — a second crossing cuts the index 78→41.", | |
| "metrics_before":{"traffic_index":78,"worst":"Old Bridge"},"metrics_predicted":{"traffic_index":41}}, | |
| "seed":99182,"t":1781231100.0} | |
| {"id":"e_000032","wish_id":"w_000009","tool":"note","args":{"text":"New families are moving in.","kind":"infill"},"seed":7,"t":1781231101.0} | |
| ``` | |
| Derived, never stored: occupancy, road graph + names, road_version, population, demand map, | |
| growth_radius. WishTrace persists per petition exactly like godseed (text, moderation, RAW LLM | |
| JSON, event ids, ms_total) → `traces/wishes.jsonl` → HF dataset (Sharing badge + NVIDIA evidence). | |
| ## Genesis (epoch 0 — engine/genesis.py is canonical; web/mock/world.json is GENERATED from it) | |
| wish_id "genesis", t = CITY_EPOCH_S. Order: roads, then buildings, then a note. | |
| Roads: | |
| - **Main St** — avenue, cx=0, cz −12..12. | |
| - **1st Ave** — avenue, cz=0, cx −12..16, EXCEPT the two river cells. | |
| - **Old Bridge** — **street** class (the bottleneck by design), cells (8,0),(9,0) over water. | |
| - **River Rd** — street, cx=13, cz −6..6. | |
| - **Elm St** — street, cz=−5, cx −8..0. | |
| - **2nd St** — street, cx=−5, cz −5..3. | |
| Buildings (anchor top-left; every footprint touches a road; engine must assert validity): | |
| - town_hall 2×2 @ (−2,−3) — faces Main St. | |
| - office 1×1, 5 floors @ (1,−2); cafe 1×1 @ (1,1); market 1×2 @ (−1,1). | |
| - park 2×2 @ (−4,1) — beside 2nd St + 1st Ave. | |
| - houses 1×1 @ (−1,−6), (−4,−6), (−6,−4) — Elm/2nd St cluster. | |
| - EAST of the river (jobs/homes that force bridge commutes from day one): | |
| factory 3×2 @ (14,−3); warehouse 2×2 @ (14,1); houses @ (14,3), (14,−5) — all on River Rd. | |
| - note: {"text":"Nemocity is listening. Ask for a building.","kind":"milestone"} | |
| Genesis population ≈ 20, jobs ≈ 95, one street-class bridge carrying every river crossing. | |
| ## LLM one-shot schemas (mind/ — small, flat, enum-heavy; a 9B nails these) | |
| **BUILD** — input: petition text (≤160 chars) + engine summary (≤600 chars: city name, pop, | |
| counts by kind, named landmarks/streets, traffic index). Output: | |
| ```json | |
| {"intent":"build","blurb":"≤140 chars, warm city-planner voice", | |
| "buildings":[{"kind":"cafe","name":"Cafe Luna","near":"the park","floors":4,"hue":35}], | |
| "decline_reason":null} | |
| ``` | |
| - buildings 1..3; kind = table enum (synonyms repaired by engine); name ≤24 chars (wordlist- | |
| checked, default derived from petition words); near = free text the ENGINE resolves; floors/ | |
| hue optional. intent "decline" (+reason) only for nonsense — moderation already gated abuse. | |
| - Parse failure after 1 retry → deterministic fallback: one house, name from petition words, | |
| blurb "The city granted a home." Something ALWAYS builds. | |
| **FIX** — input: {stats, candidates} as in the fix mechanic. Output: | |
| `{"diagnosis":"≤160","choice":"F1","blurb":"≤120"}`. Invalid choice → best predicted candidate. | |
| **Prompting** (mind/prompts.py): persona = terse municipal planner AI ("You are city hall of one | |
| shared miniature city. The engine owns all facts; you only translate petitions into permits. | |
| Build generously — a district request deserves 2–3 buildings. Never refuse a buildable thing."). | |
| Tool/kind docs rendered from engine tables (single source). NO multi-example few-shot (GODSEED | |
| leakage bug) — ONE inline example-shape JSON + "your own values, JSON only". Backend: copied | |
| ZeroGPUBackend (resident cuda at load, @spaces.GPU(duration=240), /no_think system slot, | |
| <think> strip, temperature 0.4 for JSON, max_new_tokens 700, lenient parse + 1 retry). | |
| ## HTTP API + SSE (server — keep godseed wire names; adapt fields) | |
| ``` | |
| GET /api/state → {world:{version,epoch,features:[...]}, queue, wishes_recent, server_now} | |
| POST /api/wish {text} → {wish_id, position} (petition; 400 poetic reason on moderation reject) | |
| POST /api/fix → {wish_id, position} (409 if traffic is smooth; cooldowns) | |
| GET /api/stream → SSE (X-Accel-Buffering: no) | |
| GET /api/wishes[, /{id}]→ ledger index / full trace (replay) | |
| GET /api/turn?wish_id= → {token} (cookie-bound; ZeroGPU browser-invoke dance — copied) | |
| @app.api("grant") → gradio endpoint the petitioner's browser calls (their GPU quota) | |
| GET / → web/ static (mounted AFTER launch — the route-eviction trick, copied) | |
| ``` | |
| SSE events (godseed names + ONE new event): | |
| ``` | |
| {type:"hello", world_version, epoch, server_now} | |
| {type:"queue", length, current} | |
| {type:"wish_started", wish_id, text} | |
| {type:"plan", wish_id, plan:{...raw validated LLM JSON...}} ← NEW: City Hall paperwork (NVIDIA evidence, shown in UI) | |
| {type:"tool_call", wish_id, call_index, tool, args} | |
| {type:"world_delta", feature:{...}} | |
| {type:"wish_granted", wish_id, epitaph, epoch} (epitaph = the blurb) | |
| {type:"wish_rejected", wish_id, reason} | |
| {type:"heartbeat", server_now} | |
| ``` | |
| Env vars: `CITY_BACKEND` (mock|zerogpu; llamacpp stretch), `CITY_DATASET`, `CITY_JUDGE`, | |
| `CITY_INVOKE`, `CITY_MOCK_DELAY`, `CITY_SECRET`, `CITY_HF_MODEL`, `HF_TOKEN`, `PORT`. | |
| Rate limits: 3 petitions/hr/client (cookie+IP), 1 pending/client, queue cap 50, fix cooldowns. | |
| ## Visual Bible — "Golden Hour Toybox" (miniature diorama / tilt-shift) | |
| **Thesis: the city should feel like a hand-built miniature model village on a sunlit table — | |
| small enough to love, alive enough to watch grow.** ("Build Small" made literal.) | |
| Palette (hex — `web/city/palette.js` carries these; AGENT-RFOUND owns it. constants.js holds | |
| only engine-mirrored values since gen_web.py overwrites it): | |
| - Ground: lawn `#8FC474` (±3% per-cell lightness jitter, vertex colors), park `#6FB257`, | |
| dirt lot `#CFB78F`. Water `#58AFC7` (darken 12% center). Sidewalk `#D6D2C4` w/ 0.06 u curb lip. | |
| - Roads: asphalt `#565B66` (warm dark gray, never black), markings warm-white `#F5F1E6`, | |
| center-line yellow `#EFC75E`. | |
| - Facades (8, seeded ±6° hue ±5% lightness jitter): cream `#F4E9D0`, terracotta `#E07856`, | |
| butter `#F2CC8F`, coral `#EC9AA2`, powder blue `#8FB8D8`, dusty teal `#7FB69E`, sage | |
| `#A9B98A`, brick `#BD6B6E`. Civic white `#F8F4EA` (bank/town_hall/hospital/church). | |
| - Roofs: clay `#C06B4F` (houses/church), slate `#5C6470` (apartments/school), charcoal | |
| `#3F454F` (offices/towers), gravel `#D9D5CB` (flat commercial — HVAC boxes read). | |
| - Sky: day zenith `#7BC1E8` / horizon `#FFE9C2`; night zenith `#141C36` / horizon `#2C3760` | |
| (indigo, never black). Fog = horizon color (day near 140 far 420; night near 100 far 360). | |
| - Emissives (night money colors): window glow `#FFC97A`, streetlamp `#FFD9A0`, headlight | |
| `#FFF6DC`, taillight/signal red `#FF5340`, signal amber `#FFB13D`, signal green `#43D17C`, | |
| neon teal `#45E0C0`, neon pink `#FF6FA6`, construction orange `#FF8A3D`, congestion red | |
| underglow `#E84F3D`. | |
| Lighting: ONE shadow-casting DirectionalLight (the sun), golden-hour default `#FFCE96` @ 2.4, | |
| elev 28°, az 235°; arcs with the day cycle (noon `#FFE3BB` @2.8 elev 62°; dusk `#FF9E62` @1.2; | |
| night = moon `#8FA8D8` @0.15). PCFSoftShadowMap, map 2048 (1024 small), ortho frustum fit to | |
| city bounds +10%, texel-snapped, re-fit only on growth; bias −0.00035, normalBias 0.5. Casters: | |
| building InstancedMeshes + trees ONLY. Cars/citizens/trees use instanced radial-gradient blob | |
| quads (sells the miniature). Hemisphere fill sky `#BCD7EA`/ground `#C7B18E` @0.6 day → | |
| `#2B3450`/`#161B28` @0.3 night. NO AmbientLight. RoomEnvironment PMREM, envMapIntensity 0.25 | |
| per-material. Max 4 PointLights citywide (active construction, newest build). Window emissives | |
| stagger on over ~8 s at dusk (in-shader hash threshold — zero CPU). | |
| Postprocessing (in order): RenderPass → UnrealBloomPass (DAY 0.32/0.35/0.85 — pastel facades | |
| must NOT glow in daylight; NIGHT 0.70/0.45/0.62; lerp by daylight) → tilt-shift H pass → | |
| tilt-shift V pass (+ grade folded in: saturation +8%, warm shadow lift +0.02, vignette | |
| 0.25/0.75) → OutputPass. Tilt-shift = classic separable 9-tap Gaussian, weights | |
| [.051 .0918 .12 .1531 .1633 .1531 .12 .0918 .051], per-fragment radius | |
| `maxBlur * smoothstep(bandHalf, bandHalf+falloff, abs(vUv.y - focusY))`, focusY tracks the | |
| orbit target's screen y, bandHalf 0.13, falloff 0.22, maxBlur 2.2 px × pixelRatio. Amount: 1.0 | |
| god view → lerp to 0 between camera heights 60→25 m → **0.0 in FPV (hard requirement — DoF in | |
| first person is nauseating)**. MSAA: composer target `samples: 4` (2 on small). NO BokehPass, | |
| NO SSAO — fake AO: baked dark edges in the road atlas + instanced contact-shadow quads under | |
| every building (footprint ×1.25, opacity 0.22) + tree blobs ×0.5. | |
| Roads/ground: ground = one plane per 16×16-cell chunk, vertex-colored per cell. Roads = flat | |
| quads y=0.02, ONE InstancedMesh, per-instance UV offset into a procedurally-drawn 512² canvas | |
| atlas (8 tiles: street straight, avenue straight, intersection, crosswalk approach, T-junction, | |
| parking edge, dirt, plaza paving) — dashes/stop lines/crosswalks PAINTED in the atlas, zero | |
| extra geometry, 6 px soft dark edge baked. Sidewalk curb strips instanced on road cells adjacent | |
| to zoned cells. Street furniture: instanced lamp posts every 3rd road cell (emissive head + | |
| fake light-pool quad at night), hydrants/mailbox dots at corners, 10% density. | |
| Buildings (19 kinds): assembled from a shared low-poly part kit (wall box, gabled/flat roof, | |
| awning, AC boxes, water tower, chimney, steeple, smokestack, drive-thru-style signage on shop/ | |
| cafe, fire-escape strip on apartments) — ONE InstancedMesh per part archetype with | |
| instanceColor; whole city ≈ 10–14 draw calls. Windows: ONE GLOBAL InstancedMesh citywide, | |
| emissive atlas, in-shader night flicker. Signature silhouettes matter more than detail: water | |
| towers on apartments, AC clusters, awnings + sidewalk tables on cafes (neon at night), sawtooth | |
| factory roof + smokestack, hospital cross, church steeple, stadium light masts, town_hall dome. | |
| Motion: cars = two-box kit (body + cabin, dark skirt fakes wheels), 7 seeded paint colors, | |
| right-lane offset, ease at intersections, ±2° pitch bobble on accel/brake; night headlight | |
| emissive dots + 2 short additive cone quads + taillights. Citizens = instanced capsule + head, | |
| 2-tone seeded clothes, 1.5 Hz walk bob; spawn near homes dawn / shops midday / drain home at | |
| dusk. Construction (the "watch it being built" payoff): [0,.12] dirt + dust ring → (.12,.30] | |
| orange scaffold wireframe pops to height (+ barrier cubes + blinking amber) + kit crane (mast, | |
| counterweighted jib rotating 0.15 rad/s) on ≥4-floor builds → (.30,.90] floors rise in | |
| quantized easeOutBack steps → (.90,1] scaffold drops, windows light bottom-to-top, sparkle + | |
| floating name label ("Cafe Luna — built for visitor #12"). Roads: a roller prop sweeps the path | |
| painting asphalt over 4 s; bridges add a deck-drop. Idle cranes over a skyline = a city mid-boom. | |
| Landing choreography (first paint <1 s, ZERO model calls, no spinner ever): T+0 frame one IS | |
| the screenshot — golden hour, long shadows, camera LOW (~10 m) behind a moving car on Main St, | |
| tilt-shift full. T0–3.5 s slow dolly forward along the avenue. T3.5–7 s single eased crane up + | |
| pull back to god view (38° pitch, downtown rule-of-thirds left, skyline against warm horizon). | |
| T7–10 s gentle 0.5°/s drift; a guaranteed construction event plays in view (if the live queue | |
| is empty the client keeps one scripted "welcome build" so a crane is ALWAYS rising when a judge | |
| arrives); ticker types its first headline; petition input pulses once. Any input cancels → | |
| orbit camera. Walk button drops to FPV (tilt-shift fades 0.4 s). | |
| HUD ("museum placards around a diorama" — labels NEXT TO the model, never chrome OVER it): | |
| fonts = Bricolage Grotesque 600/800 (display), Schibsted Grotesk 400/500 (UI), Spline Sans Mono | |
| (ticker/queue). Panels: warm frosted glass — `rgba(252,249,242,0.78)`, blur(14px), 1px | |
| `rgba(60,50,40,0.12)` border, radius 14px, text `#2A2620`; panels stay light at night (lit | |
| placards in a dark gallery). Layout: top-left city name + stat chips (population odometer, | |
| buildings, Traffic Index color-coded, day clock, "Built by N visitors"); bottom-center ONE | |
| rounded petition field + Build button (placeholder cycles: "a ramen shop near the park…", "a | |
| fire station downtown…"), lifecycle chips above it (queued → city hall is reading (animated | |
| ellipsis) → under construction → built, each with jump-camera link); bottom edge 28 px | |
| news-ticker (mono, LLM blurbs as headlines: "MAYOR CUTS RIBBON ON CAFE LUNA — TRAFFIC ON OLD | |
| BRIDGE UP 12%"); top-right: Walk toggle, sound toggle, PHOTO button (fades ALL UI for clean | |
| screenshots). Accents: terracotta #E07856 primary, congestion red only for traffic warnings. | |
| A "City Hall paperwork" drawer shows the raw Nemotron JSON per petition (NVIDIA evidence). | |
| Chrome <15% of screen; every panel fades 250 ms. | |
| Sound (stretch, OFF until toggled): WebAudio-synthesized only — filtered-noise traffic hum, | |
| bird blips day / cricket ticks night, soft thunk per construction floor, marimba chime on | |
| grant, single honk when a jam forms. | |
| ## FPV walk mode (web/city/fpv.js) | |
| - Vendored `lib/addons/controls/PointerLockControls.js` (r160) for look; fpv.js adds WASD | |
| (target 5 m/s, Shift 9 m/s, exponential accel/decel) integrated in the FIXED-STEP tick. | |
| - Eye height 1.65 m; camera near 0.1 far 1500 (fog covers the horizon). | |
| - Collision: occupancy-grid only — player circle r=0.35 m; test X and Z displacement | |
| independently against building cells, cancel only the blocked axis (axis-slide). Roads, | |
| sidewalks, parks, plazas walkable; water blocks (except bridge cells); clamp to city plot | |
| −2-cell margin with soft vignette hint. | |
| - Enter: "Walk" button / F key → requestPointerLock, 300 ms fade, spawn at nearest road cell to | |
| camera look-point. Exit: pointerlockchange (NOT keydown ESC) → restore orbit rig over player. | |
| - ONE camera, one scene — FPV is a controller swap via scene.js's rig-swap hook. Tilt-shift | |
| amount = 0 in FPV. Mobile: hide the Walk button on touch (v0); dual-zone joystick is stretch. | |
| ## Perf budgets (enforced, not hoped) | |
| Targets: 60 fps mid laptop, <300 draw calls, <1.2 M tris, pixelRatio ≤2 (1.7 small). | |
| Caps: buildings ≤400, cars ≤120, pedestrians ≤80, trees/props ≤1500, road cells ≤600. | |
| InstancedMesh per part-archetype (NEVER one Group per building — godseed's per-structure | |
| PointLight habit dies at city scale). Static meshes matrixAutoUpdate=false. Roads/river/ground | |
| rebuilt only on road events. A* cached per road_version, incremental re-solve ≤20 paths/frame. | |
| Degrade ladder (fps<45 for 3 s or hardwareConcurrency≤4): halve cars+peds → bloom off → | |
| shadows off → pixelRatio 1. LOD: beyond 180 m swap buildings to untextured-box instance set, | |
| hide pedestrians. All canvas atlases generated once at boot. dispose() discipline on | |
| construction particles. | |
| ## Local dev + verification | |
| - Python 3.12 venv via uv: `uv venv -p 3.12 .venv && uv pip install -r requirements.txt` | |
| (mock backend needs NO ML deps — fastapi/uvicorn/gradio/pydantic/hub only). | |
| - `CITY_BACKEND=mock PORT=7900 .venv/bin/python app.py` → http://localhost:7900 | |
| - Renderer standalone: `python3 -m http.server 8123 --directory web` + `?mock=1` | |
| (web/mock/world.json + feed.js; zero backend). | |
| - **The screenshot-and-LOOK loop is mandatory** (don't ship the first thing that renders): | |
| `node tools/shot.mjs` variants — god view day, god view night, street FPV, construction | |
| close-up, heatmap on. window.__app harness (seek/tick/stats) must keep working. | |
| - pytest: engine (placement determinism, clamps, genesis validity, traffic acceptance), mind | |
| (parse/salvage/fallback), api (SSE flow, rate limits, fix gate, injected fakes). | |
| ## Blueprint Update (v1.1 contract — June 12 PM. ⚠️ NOT part of the v0 build wave: v0 agents | |
| ## and the v0 integrator IGNORE this section entirely. It ships as its own wave after v0 verifies.) | |
| User-requested: petitions like "a Korean BBQ restaurant" must produce a building that looks | |
| UNIQUE — not just the generic cafe model. Mechanism: **bones + LLM-designed decoration**. The | |
| kit archetype is the bones (walls/floors/windows/roof — unchanged, instanced, fast); Nemotron | |
| additionally emits a compact STYLE BLUEPRINT the deterministic renderer interprets. No 3D-gen | |
| model (slow, ugly, non-deterministic, breaks zero-GPU replay); the LLM becomes the *architect* | |
| — a stronger Nemotron-load-bearing story, and the blueprint JSON shows in the paperwork drawer. | |
| Backwards compatible: `place_building.args.style` is OPTIONAL. Absent/partial style → engine | |
| fills a seeded per-kind default pack, so old events and fallback builds render unchanged-or-richer. | |
| ### style schema (LLM-emitted inside each buildings[] entry; engine clamps EVERYTHING) | |
| ```json | |
| "style": { | |
| "facade_hue": 12, "trim_hue": 350, | |
| "roof": "flat|gabled|hipped|sawtooth|dome|pagoda", | |
| "roof_color": "clay|slate|charcoal|gravel|copper", | |
| "windows": "grid|tall|arched|round|shoji", | |
| "awning": {"hues": [350, 40], "shape": "flat|scalloped"}, | |
| "sign": {"style": "hanging|vertical|rooftop|window", "neon_hue": 0, | |
| "icon": "bowl|flame|cup|leaf|star|fish|bread|music|cross|book|gear|heart|moon|paw|cart|none"}, | |
| "props": ["lanterns", "smoke_vent"], | |
| "vibe": "charcoal smoke and red neon" | |
| } | |
| ``` | |
| - Enum-heavy on purpose (9B-reliable). Engine: unknown enum → dropped/default; hues → 0..360; | |
| `props` ≤4 from: lanterns, string_lights, plants, umbrellas, barrels, crates, smoke_vent, | |
| chimney_smoke, banner, menu_board, bike_rack, flag, grill_smoke. `vibe` ≤40 chars is | |
| TOOLTIP/ticker text only — NEVER rendered as world geometry. | |
| - The sign renders the building **name** (already wordlist-moderated — the only world text) + | |
| icon glyph + neon hue. Sign textures live in ONE shared canvas atlas (256×128/slot, grown in | |
| chunks); sign quads instanced with per-instance UV offset. Icons = ~16 pre-drawn glyphs. | |
| - Props are instanced prop archetypes (a lantern string = sagging line + emissive spheres; | |
| smoke = ONE shared particle system with per-emitter offsets). All bounded: ≤4/building. | |
| - Seeded default packs per kind (engine table): e.g. cafe → menu_board+awning+window sign; | |
| factory → smoke stack already in kit. Defaults keyed by `seed` so two unstyle'd houses differ. | |
| - Prompt addition (mind): "Design the building too: pick a roof, windows, awning, a sign icon | |
| and neon color, and up to 3 props that match the petition's culture/cuisine/mood. A Korean | |
| BBQ wants flame icon, smoke_vent, grill_smoke, red neon; a ramen shop wants bowl icon, | |
| lanterns, banner." max_new_tokens 700 → 900. | |
| - File ownership for this wave: ENGINE (tools.py style clamp tables + default packs + | |
| constants), MIND (prompts/validate/mock_scripts style), RKIT (buildings.js decor layer: | |
| sign atlas, prop archetypes, roof/window variants), UI (paperwork drawer shows style; | |
| tooltip vibe). gen_web.py mirrors the style enums. | |
| ## Deploy (June 12 night / 13 morning — see hf-spaces-deploy-pins memory for war stories) | |
| 1. README YAML: `sdk: gradio`, `app_file: app.py`, **NO sdk_version** (transformers≥5.4 has | |
| nemotron_h IN-TREE, pairs with default gradio 6.18/hub≥1.5 — godseed's final shipped recipe). | |
| `models: [nvidia/NVIDIA-Nemotron-Nano-9B-v2]`, `datasets: [AndresCarreon/nemocity-world]`, | |
| tags: `build-small-hackathon, track:wood, sponsor:nvidia, achievement:offbrand, | |
| achievement:sharing, achievement:fieldnotes, thousand-token-wood, nemotron, nvidia, | |
| simulation, city-builder, world-building, three-js, multiplayer` (+offgrid/llama if the | |
| llamacpp path ships). README must LINK demo video + social post (placeholders until June 13/14). | |
| 2. requirements.txt: fastapi>=0.115, uvicorn>=0.30, pydantic>=2.7, huggingface_hub>=0.30, | |
| transformers>=5.4, accelerate, einops, sentencepiece. **NEVER pin gradio/spaces/torch** | |
| (image force-installs them). NO mamba-ssm/causal-conv1d. dtype= (not torch_dtype=). | |
| 3. app.py invariants (already copied): `import spaces` before torch when CITY_BACKEND=zerogpu; | |
| gr.Server (no gr.Blocks); launch(prevent_thread_lock=True) THEN mount_static() route | |
| eviction; ensure_worker lazy start; SSE X-Accel-Buffering: no; SameSite=None+Secure cookies. | |
| 4. Space: `build-small-hackathon/nemocity` + dataset `AndresCarreon/nemocity-world`. Secrets: | |
| HF_TOKEN; vars: CITY_DATASET, CITY_BACKEND=mock first. `request_space_hardware('zero-a10g')`. | |
| Never set_space_sleep_time on ZeroGPU. Deploy mock → verify landing/SSE/persistence → flip | |
| CITY_BACKEND=zerogpu → one real signed-in petition → tail logs. | |
| 5. If the 9B fails to load: fall back to mock granting, KEEP SERVING the city (copied posture). | |