# GODSEED β€” Architecture Contract (v1, June 12 2026) > **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). Interface drift gets fixed by the > integrator β€” but don't create drift: when in doubt, follow the schemas here exactly. ## One-liner Type a wish β†’ a tiny Nemotron god *plans aloud* and terraforms ONE persistent WebGL planet shared by every visitor. Tagline: **"Nine billion parameters. One shared world."** Track: πŸ„ Thousand Token Wood. Hackathon: Build Small (Gradio + HF). Deadline June 15. Awards targeted: NVIDIA team-judged RTX 5080 (Nemotron load-bearing), Best Agent (visible plan-act-observe + published traces), Bonus Quest (6/6 badges), Modal slot (LoRA), TTW podium, Community Choice (shared world farms likes), Judges' Wildcard. ## Non-negotiable design rules 1. **The "her" pattern** (org-celebrated judge taste): the deterministic engine owns ALL world state and facts. The LLM only interprets the wish, narrates, and picks tool calls from a small DSL. The LLM never computes geometry, never owns state. 2. **One wish at a time.** A single serialized queue. Visitors see their queue position. Latency is reframed as liturgical pacing ("the god grants one wish at a time"). 3. **The landing page must be gorgeous with ZERO GPU.** The planet renders client-side from the world-state JSON. Live inference is the second act. Judges often evaluate while GPUs sleep. 4. **Determinism end-to-end.** Every feature carries a seed; the renderer is a pure function of the ordered feature list. This makes the Genesis Log replay (see below) free. 5. **Moderation before execution.** One swastika-shaped forest during judging week ends every award. 6. **Models ≀32B total params** (hard hackathon rule). Only ONE model in the app. 7. **NAMING:** the wish archive page is called **"Genesis Log"** β€” NOT "Book of Wishes". A rival space (aether-garden) shipped a "Book of Ages" on June 11; avoid any perceived imitation. Also avoid the words "remember/remembers" in copy β€” that's their tagline. Our frame: *creation*, not memory. The god *makes*; it does not reminisce. ## Repo layout & FILE OWNERSHIP (critical β€” do not touch files you don't own) ``` godseed/ ARCHITECTURE.md (this file β€” read-only for everyone) app.py ← AGENT B (server) requirements.txt ← AGENT B README.md ← AGENT B (HF Space card w/ full YAML tags) Dockerfile.fallback ← AGENT B (in case sdk:gradio health-check fails) server/ ← AGENT B (sse.py, persistence.py, schemas.py, ratelimit.py) engine/ ← AGENT A (world.py, tools.py, queue_worker.py, moderation.py, summary.py, genesis.py) mind/ ← AGENT C (planner.py, backends.py, prompts.py, grammar.gbnf, mock_scripts.py) web/ ← AGENT D (index.html, style.css, main.js, book.html, planet/*.js, lib/ vendored three.js, mock/) tests/ ← test_engine.py (A), test_api.py (B), test_mind.py (C) tools/ ← AGENT D (shot.mjs β€” headless screenshot harness, adapted from glassblower) traces/ (runtime output β€” wishes.jsonl; gitkeep only) ``` Reference code to LIFT (read these, reuse patterns): - `/Users/andrescarreon/Documents/dev/buildsmall/glassblower/app.py` β€” FastAPI + mount_gradio_app + StaticFiles + SSE with `X-Accel-Buffering: no` header. Proven pattern. - `/Users/andrescarreon/Documents/dev/buildsmall/glassblower/web/main.js` + `web/lib/` β€” Three.js setup, vendored libs, postprocessing/bloom wiring. - `/Users/andrescarreon/Documents/dev/buildsmall/glassblower/tools/shot.mjs`, `shot-live.mjs` β€” headless Playwright screenshot harness. - Glassblower renderer learnings (hard-won): **low transmission + strong emissive** for visible glowing material (full glass transmission reads as invisible); **selective bloom threshold β‰ˆ0.66**; **decouple sim from rAF** with fixed-step `seek()`/`tick()` (headless Chromium throttles rAF). ## World model The world is an **append-only ordered list of Features**. World-at-epoch-N = features[0..N). The renderer derives ALL geometry deterministically from this list. Sky/weather are last-write-wins (but still stored as features so replay works). ### Feature (canonical JSON) ```json {"id": "f_000017", "wish_id": "w_000005", "tool": "spawn_flora", "args": {"lat": 12.0, "lon": 40.0, "radius_deg": 8, "kind": "glowgrass", "density": 0.7, "hue": 160}, "seed": 1830293847, "t": 1718200000.0} ``` `seed` is assigned by the engine: `seed = crc32(f"{wish_id}:{call_index}") & 0x7fffffff`. The JS renderer uses the same seed with mulberry32 for all scatter/noise decisions of that feature. ### Tool DSL β€” 11 tools (GBNF-friendly: flat args, numbers + enums + short strings) > The base table below is the original 9; the City Update added `spawn_life` and the Town Mode > update added `build_district` + `place_road` (full specs in those sections). The live surface is > **11 tools** β€” see `engine/tools.py` (`TOOL_NAMES`) for the source of truth. | tool | args (ranges clamped by engine) | |---|---| | `raise_terrain` | `lat` βˆ’85..85, `lon` βˆ’180..180, `radius_deg` 2..45, `height` 0.01..0.12 (planet radii), `roughness` 0..1 | | `lower_terrain` | `lat`, `lon`, `radius_deg` 2..45, `depth` 0.01..0.10, `roughness` 0..1 | | `spawn_flora` | `lat`, `lon`, `radius_deg` 1..30, `kind` ∈ trees\|glowgrass\|mushrooms\|vines\|flowers\|reeds, `density` 0..1, `hue` 0..360 | | `place_structure` | `lat`, `lon`, `kind` ∈ lighthouse\|monolith\|shrine\|village\|beacon\|arch\|tower\|warehouse\|cafe, `scale` 0.5..2.0, `hue` 0..360 | | `place_water` | `kind` ∈ stream\|pool, `path` [[lat,lon] Γ—1..5] (stream: 2..5 waypoints, becomes glowing spline on the surface β€” NOT carved; pool: path[0] center), `radius_deg` 1..10 (pool only), `hue` 160..260 | | `set_weather` | `kind` ∈ clear\|rain\|snow\|embers\|mist\|storm, `intensity` 0..1 | | `set_sky` | `palette` ∈ dawn\|dusk\|night\|aurora\|ember\|void\|gold, `star_density` 0..1, `moons` 0..3 | | `inscribe_wish` | `text` ≀90 chars (the wish, possibly poeticized), `style` ∈ orbit\|stone | | `spawn_life` | `lat`, `lon`, `radius_deg` 1..20, `kind` ∈ carts\|birds\|fireflies, `count` 1..12 (integer), `hue` 0..360 | Engine **clamps** out-of-range numbers (doesn't reject) and rejects only unknown tools/enums with a terse observation string the model can react to. ### Genesis state (epoch 0 β€” MUST match between engine and renderer mock) ```json [ {"id":"f_000000","wish_id":"genesis","tool":"set_sky","args":{"palette":"void","star_density":0.9,"moons":1},"seed":7,"t":0}, {"id":"f_000001","wish_id":"genesis","tool":"raise_terrain","args":{"lat":12,"lon":40,"radius_deg":28,"height":0.05,"roughness":0.7},"seed":11,"t":0}, {"id":"f_000002","wish_id":"genesis","tool":"place_structure","args":{"lat":14,"lon":38,"kind":"monolith","scale":1.4,"hue":270},"seed":13,"t":0}, {"id":"f_000003","wish_id":"genesis","tool":"inscribe_wish","args":{"text":"speak, and it will be made","style":"orbit"},"seed":17,"t":0} ] ``` A barren obsidian world, one monolith, an orbiting invitation. Gorgeous and zero-GPU by design. ## Agent protocol (the Mind) Real plan-act-observe loop (Best Agent evidence), GBNF/JSON-constrained: 1. **Reading** (free-text generation, streamed token-by-token, ≀80 tokens): the god interprets the wish aloud β€” mood, intent, plan sketch. This is the "plans aloud" wow moment. 2. **Turn loop** (grammar-constrained JSON, max 7 turns). Each turn the model emits exactly one of: - `{"thought": "≀160 chars", "call": {"tool": "...", "args": {...}}}` - `{"thought": "≀160 chars", "done": true, "epitaph": "≀120 chars"}` After each call the engine returns a terse observation (`"ok: mountains risen at (12,40)"` / `"rejected: unknown kind 'cactus'"`) which is appended to the context before the next turn. Planner interface (AGENT C implements; AGENT A's queue worker drives it): ```python class Planner: async def grant(self, wish: str, world_summary: str, act: Callable[[dict], Awaitable[str]], # executes a tool call, returns observation emit: Callable[[dict], Awaitable[None]] # streams SSE events (thought_token, etc.) ) -> WishTrace ``` Backends behind `GODSEED_BACKEND` env (mind/backends.py): - `mock` β€” deterministic keywordβ†’plan scripts with seeded variety + artificial token delays. Local dev + headless demos. Must exercise every tool. - `llamacpp` β€” llama-cpp-python, **NVIDIA Nemotron-3-Nano-4B GGUF Q4_K_M** (~2.5GB, fits free 16GB CPU tier), native GBNF grammar. This is the v0 SHIP backend (earns Off the Grid + Llama Champion badges; real model, no GPU bill). Model path via `GODSEED_GGUF` env; download in code from HF hub if missing. - `zerogpu` β€” transformers + `@spaces.GPU`, **NVIDIA Nemotron-Nano-9B-v2** (the LIVE backend; in-tree nemotron_h class, trust_remote_code=False, kernel-free native Mamba-2 path). Constrained decoding via prompt + JSON validation + 1 retry (no GBNF in transformers; keep a strict parser). NOTE (June 12): the Nemotron-3 4B/30B repos hard-require remote code + mamba-ssm kernels, which cannot import on the ZeroGPU image β€” they are NOT usable on this backend. Moderation (engine/moderation.py, AGENT A): layered β€” (1) charset/length limits (≀140 chars, printable); (2) wordlist/regex for slurs, sexual content, hate symbols; (3) LLM yes/no judgment via the same backend (grammar-constrained `{"allowed": bool, "category": "..."}`), **default-deny on uncertainty or parse failure**. Rejections are poetic but final ("The god declines this wish."). ## Queue `engine/queue_worker.py` (AGENT A): single global asyncio worker. `submit(text, client_id) β†’ (wish_id, position)`. Caps: 1 pending wish per client, 3/hour per client (B's ratelimit.py supplies client_id from cookie+IP hash), queue max 50 ("the god is overwhelmed; return at dusk"). Worker: pop β†’ moderate β†’ planner.grant() β†’ persist trace β†’ emit `wish_granted`. ## SSE protocol (server/sse.py β€” single `/api/stream` broadcast to all clients) ``` {type:"hello", world_version, epoch} {type:"queue", length, current: {wish_id, text_preview} | null} {type:"wish_started", wish_id, text} {type:"thought_token", wish_id, text} ← streamed reading + turn thoughts {type:"tool_call", wish_id, call_index, tool, args} {type:"world_delta", feature: {…Feature…}} ← renderer applies live {type:"wish_granted", wish_id, epitaph, epoch} {type:"wish_rejected", wish_id, reason} {type:"heartbeat"} ← every 15s ``` Set `X-Accel-Buffering: no` on the SSE response (proven necessary on Spaces β€” see glassblower). ## HTTP API (app.py, AGENT B) ``` GET /api/state β†’ {world: {version, epoch, features:[...]}, queue: {...}, wishes_recent:[...]} POST /api/wish {text} β†’ {wish_id, position} (400 w/ poetic reason if moderation fast-path rejects) GET /api/stream β†’ SSE GET /api/wishes β†’ [{wish_id, text, epitaph, epoch, ts}] (Genesis Log index) GET /api/wishes/{id} β†’ full WishTrace (for deterministic replay) GET / β†’ web/index.html (StaticFiles) GET /book β†’ web/book.html /panel β†’ minimal gr.Blocks mounted via mount_gradio_app (satisfies "Gradio app" rule: altar console β€” submit a wish, view raw world JSON, link to traces dataset) ``` `app.py` must work under plain `python app.py` (uvicorn.run, port = `$PORT` or 7860) β€” HF sdk:gradio runs the script directly. ZeroGPU REQUIRES sdk:gradio, so make this work; Dockerfile.fallback is break-glass only. ### WishTrace (persisted, one JSONL line per granted wish β†’ traces/wishes.jsonl) ```json {"wish_id":"w_000005","text":"...","submitted_at":0,"moderation":{"allowed":true,"category":null}, "reading":"...","turns":[{"thought":"...","call":{...}|null,"observation":"..."}], "epitaph":"...","feature_ids":["f_000017"],"model":"...","backend":"llamacpp","ms_total":48211} ``` Persistence (server/persistence.py, AGENT B): rebuild world from JSONL on boot (genesis + every trace's features). After each granted wish: append JSONL; push `traces/wishes.jsonl` to an HF dataset repo (`HF_TOKEN` + `GODSEED_DATASET` envs; skip silently if unset β€” local dev). The dataset IS the persistence across Space restarts (download on boot if local file missing) AND the Sharing is Caring badge AND Best Agent evidence. ## Renderer (web/, AGENT D β€” the WOW is here; budget most effort) Three.js (vendor from glassblower web/lib). Composition: - **Planet**: icosphere (subdiv ~6), vertex displacement = sum of feature gaussians (raise/lower) modulated by seeded noise Γ— roughness. On `world_delta`, lerp the displacement in over ~2s (terrain visibly *rises*). Obsidian-dark base material, biome tinting near flora. - **Flora**: InstancedMesh per kind, emissive, seeded scatter within radius_deg, scale-in animation staggered by seeded delay. Glowgrass = shader points; trees/mushrooms = low-poly kit. - **Water**: TubeGeometry along great-circle spline through waypoints, emissive glow (low transmission + emissive β€” glass learnings), gentle flow shader. Pools = glowing discs w/ ripple. - **Structures**: small low-poly procedural kit (lighthouse with rotating beam, monolith, shrine, village cluster w/ lit windows, beacon, arch), emissive accents. - **Weather**: GPU particles (rain streaks / snow / embers / mist planes / storm = rain+flashes). - **Sky**: palette-driven gradient + stars (Points, density param) + 0..3 moons + aurora bands (palette `aurora`). Background environment affects planet lighting tint. - **Inscriptions**: `orbit` = glowing text ring slowly orbiting (canvas texture on curved plane); `stone` = text plaque at lat/lon. - **Bloom**: UnrealBloomPass, selective, threshold β‰ˆ0.66. Cap pixelRatio ≀2. Must hold ~60fps on a laptop, degrade gracefully on phones (reduce instances/subdiv). - **The god's gaze**: on `tool_call`, camera eases to the target lat/lon, then resumes slow auto-orbit. This is the demo money-shot. - **Sim/replay core**: fixed-step `tick()`/`seek(t)` decoupled from rAF (headless-safe, makes Genesis Log replay + demo capture free). UI (index.html): full-bleed canvas. Wordmark "GODSEED" (letterspaced display serif β€” e.g. Cormorant Garamond via Google Fonts), thin HUD: wish input ("make a wish"), queue chip ("3rd in line Β· the god grants one wish at a time"), thought ticker (mono font, streaming), epoch counter ("Epoch 17 Β· 23 wishes granted"). Deep-space black, gold/violet accents. NO generic AI-gradient-purple-on-dark-card look β€” aim for liturgical-modern, like a Kinfolk issue about a small god. Footer: "Genesis Log" link. book.html (Genesis Log): list of granted wishes (epitaphs); click one β†’ replay THAT wish: render world at features[0..K), then animate wish K's deltas with its recorded thoughts replayed in the ticker. Pure client-side from `/api/wishes/{id}`. Zero GPU. Shareable URL `#w_000005`. `web/mock/` : a static mock world JSON (genesis + ~10 varied wishes exercising ALL 8 tools) + a scripted SSE-like event feed (JS array) so the renderer is fully developable + screenshottable with NO backend: `python3 -m http.server 8123 --directory web` + `?mock=1`. **AGENT D self-verifies**: adapt glassblower `tools/shot.mjs`, screenshot at multiple sim times, LOOK at the PNGs, iterate until genuinely beautiful. Don't ship the first thing that renders. ## Local dev - Python 3.12 venv via uv (`uv venv -p 3.12 .venv && uv pip install -r requirements.txt`). System 3.14 is too new for the ML stack. - `GODSEED_BACKEND=mock PORT=7891 .venv/bin/python app.py` - requirements.txt: keep base CPU-light (fastapi, uvicorn, gradio, pydantic, huggingface_hub, llama-cpp-python). torch/transformers/spaces go in a clearly-marked ZeroGPU block (commented or extras) until the benchmark decision. ## City Update (v1.1 contract β€” June 12 PM) Player-requested ("building games": cafeterias, big buildings, warehouses, cars). Three additions, same diorama soul. Backwards compatible: old features render unchanged; additions are additive enums + ONE new tool (copy that says "exactly 8 tools" must be updated to 9 everywhere: README, ARCHITECTURE intro, prompts). 1. **New structure kinds** (place_structure enum += `tower`, `warehouse`, `cafe`): - `tower` β€” tall slender multi-tier block, dense lit windows, slow-pulsing rooftop light. The "big building". - `warehouse` β€” long low gabled hall, one big glowing door, sparse windows. - `cafe` β€” small warm box: awning plane, spill of warm light on the ground, 2-3 tiny table dots outside. Hue tints the glow. All three: low-poly procedural kit pieces matching existing style (emissive accents, seeded variation), instanced where possible. 2. **New tool #9 `spawn_life`** β€” moving inhabitants: `{lat, lon, radius_deg 1..20, kind: carts|birds|fireflies, count 1..12, hue 0..360}` - `carts` β€” tiny lantern-cart sprites that GLIDE along the nearest stream path within radius (fallback: a seeded loop around the anchor). Distant traffic at dusk. Speed slow, constant, sim-time driven. - `birds` β€” a small flock circuiting above the terrain at low altitude, seeded loop, slight bob. - `fireflies` β€” drifting glow points inside the radius, near the ground. Renderer: new `web/planet/life.js`, instanced, deterministic seeded paths driven by tick(simTime) (replay-safe, headless-safe). Engine: spec + clamps in tools.py; features append-only as usual. GBNF grammar + mock scripts + tool docs must all gain the tool (docs auto-render from as_dict βœ“). 3. **Cinematic descent** ("visit it"): - `rig.descend(targetDir, {duration})` β€” swoop from orbit to ~1.06R above ground (clears terrain via the analytic height fn), slow 20-30Β° glide arc over the target with gentle look-ahead, then ease back to orbit. Fully sim-time deterministic. - Triggers: a "descend" link on the epitaph card after a wish is granted (targets the wish's last landed feature), and on Genesis Log replays after the final delta. ESC / click cancels. No free controls, no collision β€” it's camera liturgy, not an FPS. Build ownership when this ships: (A) engine/tools.py + mind/grammar.gbnf + mind/mock_scripts.py + tests; (B) web/planet/structures.js (new kinds only); (C) web/planet/life.js + web/planet/camera.js + main.js/book.js descend hooks. Copy updates (README 8β†’9 tools etc.) ride with (A). ## Town Mode (v1.2 contract β€” June 12 PM, "Town on the planet") User feedback: a city wish produced one tower on the far side of a planet-sized sphere β†’ reads empty. The fix is a deliberate experience shift: the DEFAULT view is a town you build up close (SimCity feel); the full planet becomes the dramatic zoom-out reveal. Append-only world preserved; genesis UNCHANGED (the live shared world must keep rebuilding identically). All additive. ### The town anchor (engine + renderer compute IDENTICALLY) `townCenter(world)` = the seeded-weighted centroid (unit-vector mean, renormalized) of all `place_structure` + `build_district` feature directions, EXCLUDING genesis. Fallback when none exist: the genesis monolith at lat 14, lon 38. Engine exposes it in the world summary; the renderer uses it for the default camera. A town gets a deterministic name from `crc32(round(lat),round(lon))` β†’ a small curated name list (e.g. "Lowmere", "Ashford", "Tessen") so the summary + HUD can say "the town of X". ### Density comes from each tool call, not from many (the 9B reliably plans only ~2-3 calls) New tools (now **11** β€” update every "9 tools" copy β†’ 11; list build_district + place_road): | tool | args | |---|---| | `build_district` | `lat`,`lon`,`radius_deg` 2..15, `density` 0..1, `hue` 0..360 β€” renderer places a SEEDED neighborhood: a cluster of small houses on a rough grid, footpath/road connectors between them, and 1 occasional civic building. ONE successful call = a visible block. This is the SimCity unlock. | | `place_road` | `path` [[lat,lon] Γ—2..6] β€” a paved, faintly-lit road spline laid ON the surface; visually DISTINCT from `place_water` glowing streams (warm stone/amber, matte, dashed centerline feel β€” not emissive cyan). | `place_structure.kind` enum += `bank`, `market`, `house` (single landmark homes/civic buildings; the existing low-poly kit conventions). Full set: lighthouse|monolith|shrine|village|beacon|arch|tower| warehouse|cafe|bank|market|house. ### Steering the god to GROW THE TOWN (mind/prompts.py + engine/summary.py) - summary.py: name the town + its center + building count ("The town of Lowmere stands near (14,38): 3 towers, a cafΓ©, 12 houses."). When the wish asks for buildings/a city, the god should ADD to the existing town, not scatter β€” build near townCenter, connect with `place_road`, and reach for `build_district` for neighborhoods. Persona gains: "You are growing one town. New buildings rise beside the old ones, linked by roads β€” never alone in the wilderness." - This is steering, not enforcement (keeps the "god decides" purity). The camera following density means even a stray build is visible; over wishes the town concentrates. ### Camera = two modes (web/planet/camera.js + main.js) - **TOWN view (DEFAULT on load):** camera sits LOW and close, framing a ~28Β° cap around townCenter β€” buildings read large, like standing over a model town. This is what visitors see first now (not the full orbit). - **WORLD view (the reveal):** a HUD control "behold the world ↕" eases out to the full-planet orbit (today's gorgeous wide shot) and back. The demo money-move: build the town, then pull back to show it's one patch on a shared planet. - On each `world_delta`/grant, frame the new feature WITHIN the town zoom (don't fly to the far limb). - The existing descent stays (epitaph card) β€” now it descends within the town. ### Tame the god-needle The orbital "god instrument" needle currently dominates the frame (huge gold double-cone). In TOWN view it must be small/high or hidden; show it prominently only during active granting (it "writes" the wish) and in WORLD view. Scale it down ~60% at rest. ### Ownership for the Town Mode build - ENGINE+MIND (A): engine/tools.py (build_district, place_road, +3 structure kinds), engine/summary.py (town name+center+counts), engine/world.py (_observation for new tools), mind/grammar.gbnf, mind/mock_scripts.py (town-growth scripts), mind/prompts.py (grow-the-town steering + 11-tool copy), tests; README/ARCHITECTURE 9β†’11 copy. - RENDERER (B): web/planet/structures.js (bank/market/house kit), web/planet/district.js (NEW β€” seeded house+road neighborhood), web/planet/roads.js (NEW β€” paved road splines) OR fold roads into water.js styling, web/planet/world.js wiring, needle scale-down (find it β€” likely sky.js or scene.js). - CAMERA+HUD (C): web/planet/camera.js (TOWN/WORLD modes + townCenter framing), web/main.js + book.js (default TOWN view, "behold the world" toggle, frame-within-town on delta), web/index.html + style.css (the toggle control). townCenter helper shared via web/planet/util.js. ## Deploy plan (not in this build phase β€” for context) 1. Personal benchmark Space first: llamacpp 4B on free CPU + (separately) ZeroGPU 30B-A3B; measure tok/s before committing the headline backend. 2. Org Space `build-small-hackathon/godseed`, sdk:gradio. README YAML: full track + sponsor + badge tags (copy exact tag strings from case0/her READMEs β€” AGENT B: fetch `https://huggingface.co/spaces/build-small-hackathon/case0/raw/main/README.md` and mirror tag style). 3. v0 ships TONIGHT (June 12) on llamacpp 4B. Social + like-farming starts immediately.