# The Settled Reach A top-down life-sim — asymmetric information, occlusion-based perception, single-character perspective, Rimworld-style storyteller. Systems interactions like The Sims, combat and visuals like single-character Rimworld, world generation inheriting patterns from Dwarf Fortress and NMS, economy inspired by X4/EVE. Many systems interactable and ready for player participation, but capable of being ignored — the world is alive for any given run. Set in an original science fiction universe. Godot 4 client + Rust/bevy_ecs simulation server via subprocess/IPC. **Official Title:** The Settled Reach **Repository name:** settled-reach **Version source of truth:** `project.yaml` (root `version` field, scheme: `0.1.{sprint_number}`) ## Project Structure ``` client/ # Godot 4 client server/ # Rust/bevy_ecs simulation server tooling/ # Build tools, scripts, asset pipelines tests/ # Integration and end-to-end tests docs/ # Architecture, design, briefings, sprints, workshops db/ # Schema + seed data (connectors moved to tooling/db/) .claude/ # Agents, skills, rules decisions/ # Decision domain files (D-NNN confirmed, Q-NNN open, R-NNN rejected) ``` Full annotated tree: `.claude/rules/project-structure.md` ## DevOps See [docs/DEVOPS.md](docs/DEVOPS.md) for build, test, lint, and CI procedures. All development operations go through the top-level `Makefile` — run `make` for a summary of targets. ## Development Cascade — First Things First Development follows a strict cascade. Each phase has a concrete deliverable. **Do NOT discuss, design, or implement detail from a later phase while an earlier phase is incomplete.** If you encounter references to later-phase detail (room grammar, NPC bundles, heritage tokens, etc.) in documents or decisions, either ignore them silently and stay at the correct level, or flag that the reference is dragging attention to the wrong scope level and suggest it be rephrased or moved. | Phase | Focus | Deliverable | |-------|-------|-------------| | 1 | Wiki content complete — all planets, moons, stations, heightmaps, artwork | Implant-ready Godot map of the Reach with click-throughs + wiki/GTTR popups | | 2 | Economics layer — supply/demand, transport, political/social pressure, corporations, supply chains | Economics spreadsheets/graphs with runtime-tweakable simulation | | 3 | Planetary/moon maps & station layouts — cities, rivers, mountains, roads, biomes, rail | Atlas of the Reach (implant app) | | 4 | Player control scheme — 2-floor test map, character rendering, walls/stairs/doors, lighting | Player viewport with final-version assets | | 5 | World generation (tile/chunk/block) — walkable world, parallel asset pipeline | Walkable generated world + asset catalog | | 6 | Detail coloring — room-level NPC population, cultural room grammar | Only when the world is walkable | **v0.2 target is dropped.** No scoping negotiations. Build the base systems fully. ## Work Modes ### Sprint mode (default) Agents work autonomously on sprint branches. Team lead coordinates via tasks and messages. Human reviews PRs from main. Standard `/sprint-start` → `/pr-push` → `/pr-review` lifecycle. ### Pair session Human and Claude work together interactively on a single task. No background agents, no autonomous work. Used for load-bearing architecture changes where the human needs to make judgment calls as the work progresses — not approve a finished result. **Rules:** - No `Agent` spawns, no `run_in_background`. One thread of work. - Propose one change at a time. Wait for human reaction before continuing. - Explain what you're about to do and why before doing it. - After each change, verify together (compile, test, inspect) before moving to the next. - The human is a participant, not a reviewer. Ask questions, surface tradeoffs, flag risks in real-time. **When to use:** Ticket description says "pair session", or the work touches foundational systems where a wrong call is expensive to undo (tick cycle architecture, protocol design, data model migrations, build pipeline rewrites). ## Agent Instructions ### Team boundaries **Your team is determined by your sprint branch** (e.g. `sprint-31/server` → server team). - All file paths are relative to the repo root (e.g. `server/src/bridge/types.rs`). - **Stay within your team's scope.** Server team modifies `server/`. Client team modifies `client/`. Copy team modifies `wiki/`, `docs/atlas/`, `content/`. Shared directories (`docs/`, `decisions/`) are readable by all teams. - **Do NOT modify files outside your team scope** unless the ticket explicitly requires it. - **Never chain git commands** in a single Bash call (e.g. `git add ... && git commit ...`). Always run `git add` and `git commit` as **separate sequential Bash calls**. - **Stale git lock files:** If a `git` command fails with `index.lock: File exists`, you may remove the lock file at `.git/index.lock` (or `.git/worktrees//index.lock` if in a worktree). ### Database The ticketing database (`settledreach.db`) is accessed via `SR_DB_PATH` env var (set in `.claude/settings.json`). A backup is committed to `docs/backups/settledreach.db.backup` via main only. ### Before starting work 1. Read your sprint briefing at `docs/sprints/sprint-N/{team}.md` for current tasks 2. Use `tooling/db/ticket show ` for full ticket details 3. Read the relevant `decisions/*.md` domain file(s) referenced in the briefing 4. Background context: `docs/briefings/{your-name}.md`, `docs/discussions/` ### CLI tools **Prefer CLI wrappers over raw SQL.** Never use the `sqlite3` CLI — it crashes in Claude Code (std::bad_alloc). Use the wrapper scripts instead. | Tool | Command | Full reference | |------|---------|----------------| | Tickets | `tooling/db/ticket list`, `show`, `create`, `assign` | `/ticket` skill | | Sprints | `tooling/db/sprint status`, `start-work`, `prepare` | `/sprint-start` skill | | SQL queries | `tooling/db/sqlite-query "SELECT ..."` | — | | SQL writes | `tooling/db/sqlite-exec "UPDATE ..."` | — | | Decisions | `tooling/db/decision next`, `claim`, `check-dupes` | — | ### Testing preferences - **Prefer live Gauntlet testing over mocks.** For visual tests and rendering verification, use the full client/server pipeline (`--test-mode` + `SR_LIVE=1`) instead of TestHarness mocks. The Gauntlet test world produces production-identical data. Mocks can mask rendering bugs by taking different code paths. - **Gauntlet rooms are immutable.** Never modify existing rooms — new systems get new rooms. This ensures StableId determinism and fixture stability. - Three test tiers: (1) Live server — highest fidelity, (2) MessagePack replay via `Protocol.decode_snapshot()` — for unreachable rooms, (3) TestHarness mock — for UI-only tests where fog data doesn't matter. - `make fixtures-gauntlet` regenerates real server snapshot fixtures from the Gauntlet world. ### Implant UI component library (D-169, D-170) The implant UI system — all diegetic neural overlay panels — lives at `client/ui/implant/`. Components are Godot Control scenes styled via a shared `Theme` resource (`default_implant.tres`). Do not hand-roll implant panel layouts; compose from the library. **Components:** `ImplantPanel` (root container), `ImplantHeader` (title + subtitle), `ImplantSeparator` (horizontal rule), `ImplantDataRow` (key/value row, optional color), `ImplantTextBlock` (RichTextLabel for wrapping text). **Theme resource:** `client/ui/implant/default_implant.tres` — defines semantic color roles (`PRIMARY_TEXT`, `DIM_TEXT`, `ACCENT_ACTIVE`, `ACCENT_POSITIVE`, `ACCENT_NEGATIVE`, `ACCENT_WARNING`, `SEPARATOR`), spacing, and font sizes. Swap the entire `.tres` to change implant hardware appearance at runtime. **HUD visibility (D-170):** `client/scripts/autoloads/hud_groups.gd` manages z-index layering. Modes: `GAMEPLAY` (z=0), `INSERT` (z=10), `FULLSCREEN` (z=20), `MODAL` (z=30). App paths are hierarchical: `implant/map`, `implant/wiki/gttr`, etc. Opening any `implant/*` app occludes gameplay; closing returns to gameplay. Key API: `open_app()`, `close_app()`, `toggle_app()`, `is_app_active()`. Emits `gameplay_occluded` signal so renderers can pause. **GameplayRenderer base class:** `client/scripts/rendering/gameplay_renderer.gd` — extends `Node2D`. Subclasses override `_gameplay_process()` and `_gameplay_draw()`. Connected to `HudGroups.gameplay_occluded` to pause when the implant is fullscreen. Used by `CursorRenderer`, `EntityRenderer`, `FogEntities`, `SoundIndicatorRenderer`, `WorldRenderer`. ### GDScript conventions **Autoload parse-order rule:** Autoload scripts (`client/scripts/autoloads/`) compile before global `class_name` scripts are registered. Referencing a `class_name` type directly in an autoload causes a parse-time "not declared" error. Pattern: - Declare fields untyped: `var my_field = null` (comment the intended type) - Do **not** reference `class_name` types at the top level or in `_ready()` of autoloads - In method bodies called at runtime (e.g. `apply_snapshot`), use `load()` inline — by then the script is cached and `load()` returns the cached resource without reloading: `var CVD := load("res://scripts/rendering/character_visual_descriptor.gd")` - Do **not** cache the `load()` result in `_ready()` — `_ready()` fires during autoload init, before the target script is in the resource cache, causing an actual file reload that breaks self-references in scripts using their own `class_name` `game_state.gd` (`character_visual_descriptor` field) and `sim_bridge.gd` (`harness` field) follow this pattern. ### File conventions - Decisions: domain files in `decisions/` (see `decisions/README.md` for index) - Decision IDs: `D-NNN` (confirmed), `Q-NNN` (open questions), `R-NNN` (rejected) - **Claim IDs before writing:** `tooling/db/decision claim D "title"` — prevents ID collisions across parallel branches - Diagrams: `.d2` source + `.png` renders in `docs/diagrams/{category}/`. Create or update diagrams via `/d2-diagram` when D-records are added or modified. - Discussion rounds: numbered sequentially, archived to `docs/discussions/` when complete - Briefings: one per agent, updated after decision-producing rounds - Tickets: managed via `tooling/db/ticket` CLI or `/ticket` skill