diff --git a/.claude/agents/README.md b/.claude/agents/README.md index 68db37a8e..8fe8444d7 100644 --- a/.claude/agents/README.md +++ b/.claude/agents/README.md @@ -107,7 +107,7 @@ Synthesize findings. ## Agent usage notes ### Troblum (Technical Consultant) -- **Always paired with or supporting Tyre** - never works alone on architecture decisions +- **Solo** for performance profiling, benchmarking, and technology stress-tests; **paired with Tyre** for architecture second opinions — never the sole voice on an architecture decision - Spawned for specific evaluation sidequests, not open-ended discussion - Blunt, data-driven, will challenge Tyre's assumptions with evidence diff --git a/.claude/settings.json b/.claude/settings.json index 9ec81c2b8..5389fdfef 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -54,8 +54,10 @@ "Bash(godot4:*)", "Bash(gdformat:*)", - "Bash(tooling/atlas *)", + "Bash(tooling/atlas:*)", "Bash(tooling/validate-ron:*)", + "Bash(tooling/godot-cold-parse:*)", + "Bash(tooling/pr-watchlist-diff:*)", "Bash(chmod *)", "Bash(ls *)", @@ -80,9 +82,32 @@ ], "deny": [ "Bash(git push --force *)", + "Bash(git push --force:*)", + "Bash(git push --force)", "Bash(git reset --hard *)", + "Bash(git reset --hard:*)", + "Bash(git reset --hard)", "Bash(git clean -f *)", - "Bash(rm -rf *)" + "Bash(git clean -f:*)", + "Bash(git clean -f)", + "Bash(rm -rf *)", + "Bash(rm -rf:*)" + ], + "ask": [ + "Edit(.claude/settings.json)", + "Write(.claude/settings.json)", + "Edit(.claude/settings.local.json)", + "Write(.claude/settings.local.json)", + "Edit(.claude/hooks/**)", + "Write(.claude/hooks/**)", + "Edit(.config/hooks/**)", + "Write(.config/hooks/**)", + "Edit(.worktrees/**/.claude/settings.json)", + "Write(.worktrees/**/.claude/settings.json)", + "Edit(.worktrees/**/.claude/hooks/**)", + "Write(.worktrees/**/.claude/hooks/**)", + "Edit(.worktrees/**/.config/hooks/**)", + "Write(.worktrees/**/.config/hooks/**)" ] }, "hooks": { diff --git a/.claude/skills/pr-process/SKILL.md b/.claude/skills/pr-process/SKILL.md index bd23cf40b..ee79b3310 100644 --- a/.claude/skills/pr-process/SKILL.md +++ b/.claude/skills/pr-process/SKILL.md @@ -285,9 +285,13 @@ git diff --stat main... Draft title (`(): `, max 70 chars) and description. ```bash -# NB: omit --repo — an explicit slug makes tea skip local-repo setup and -# pr create dies with "local repository required". Run from the MAIN checkout; -# tea infers the repo from origin. (.claude/rules/tea-cli.md) +# NB — both halves matter (.claude/rules/tea-cli.md): +# 1. omit --repo: an explicit slug makes tea skip local-repo setup and +# pr create dies with "local repository required"; +# 2. run THIS ONE COMMAND from the MAIN checkout, not this worktree — +# tea's go-git can't read a worktree's .git *file*. From the worktree: +# cd "$(git rev-parse --path-format=absolute --git-common-dir)/.." +# for this command, then return here for the remaining steps. tea pr create \ --login schweitz \ --title "" \ diff --git a/.claude/skills/pr-review/SKILL.md b/.claude/skills/pr-review/SKILL.md index 8f1d09010..b14663e79 100644 --- a/.claude/skills/pr-review/SKILL.md +++ b/.claude/skills/pr-review/SKILL.md @@ -6,7 +6,7 @@ description: > /pr-review. Spawns reviewers matched to the branch type (code, copy, visual, audio) in parallel. Reports approve/reject with inline comments. user-invocable: true -allowed-tools: Bash, Read, Grep, Glob, Task, Write +allowed-tools: Bash, Read, Grep, Glob, Agent, Write --- # PR Review Skill diff --git a/TEAM.md b/TEAM.md index b97fa3ba8..ee5703477 100644 --- a/TEAM.md +++ b/TEAM.md @@ -31,16 +31,21 @@ |-------|------|-------|-------------| | **SI** | Project Manager & Scrum Master | Sprint planning, ticket management, turning decisions into executable work. Manages `/ticket` skill. | Always (from now) | +## Implementation Team (active) + +| Agent | Role | Focus | When active | +|-------|------|-------|-------------| +| **DUDLEY** | Server Developer | Game server, ECS, simulation loop, world state management. | Active — server/ is the main Phase-4 cascade workstream | +| **STIG** | UI Developer | HUD, menus, insert/minimap, diegetic UI per D-013. | Active — client UI work as needed | +| **JUSTINE** | Polish & Deploy | Build pipelines, packaging, performance optimization, release prep. | Active — build/validation tooling | +| **MELLANIE** | Copywriter | In-game text, UI copy, tooltips, flavor text, lore entries. | Active — content batches | + ## Standby Team (activate when needed) | Agent | Role | Focus | When active | |-------|------|-------|-------------| -| **STIG** | UI Developer | HUD, menus, insert/minimap, diegetic UI per D-013. | UI implementation phase | -| **DUDLEY** | Server Developer | Game server, ECS, simulation loop, world state management. | Server implementation phase | | **OSCAR** | Networking Developer | Multiplayer networking, client-server protocol, sync. | Networking implementation phase | -| **JUSTINE** | Polish & Deploy | Build pipelines, packaging, performance optimization, release prep. | Pre-release phase | | **INIGO** | Sound Designer | Soundscape design, ambient layers, diegetic audio cues, audio propagation. | Audio implementation phase | -| **MELLANIE** | Copywriter | In-game text, UI copy, tooltips, flavor text, lore entries. | Content creation phase | ## Agent Briefings diff --git a/docs/briefings/dudley.md b/docs/briefings/dudley.md index ba699bba3..9d2fb21df 100644 --- a/docs/briefings/dudley.md +++ b/docs/briefings/dudley.md @@ -1,6 +1,6 @@ --- title: "Dudley - Project Briefing" -description: "Backend/engine implementation agent on standby — covers Rust simulation server, ECS model, and bevy_ecs architecture decisions" +description: "Server implementation agent — Rust/bevy_ecs simulation server, ECS model, world-generation cascade; the project's most active workstream" type: briefing status: active agent: Dudley @@ -8,14 +8,19 @@ updated: 2026-03-13 --- # Dudley - Project Briefing -Last updated: 2026-03-13 +Last updated: 2026-07-13 ## Current Project State Check active work: `pql ticket list --status in_progress` Check your tickets: `pql ticket list --assigned dudley` ## Status -STANDBY. This briefing will be populated when backend/engine implementation begins. Dudley activates when simulation implementation work is assigned. +ACTIVE. The Rust/bevy_ecs simulation server (`server/`) is the project's most +active implementation workstream — the Phase-4 world-generation cascade (D-166, +D-243) runs through it. Dudley is the default implementer for server batches +selected by `/whats-next`; recent work includes the Layer-5 tile fill and the +D-253 region transient model (PR #174). Build/test via the top-level Makefile; +the pre-push gate runs fmt/clippy/full cargo test on every push. ## Key Decisions for Your Domain Read `decisions/architecture.md` (primary) and `decisions/content.md` (NPC model). Key decisions: diff --git a/docs/design/diagram-wireframe-conventions.md b/docs/design/diagram-wireframe-conventions.md index 1e0334229..b8333acdf 100644 --- a/docs/design/diagram-wireframe-conventions.md +++ b/docs/design/diagram-wireframe-conventions.md @@ -8,8 +8,9 @@ deleted (CLAUDE-CLEANUP §2 / T-1101). ## d2 diagrams (`/d2-diagram`) **Output taxonomy:** `.d2` source + `.png`/`.svg` renders under -`docs/diagrams/{category}/{name}.d2` — categories in use: `architecture/`, `ui/`, -`entity/`. Create/update a diagram whenever a D-record is added or modified. +`docs/diagrams/{category}/{name}.d2` — categories in use: `architecture/`, +`data-flow/`, `design/`, `entity/`, `state/`, `ui/`. Create/update a diagram +whenever a D-record is added or modified. **Agent routing** (who to task with a diagram, by domain): - **Qatux** — architecture decision records, system-overview + data-flow diagrams. @@ -19,8 +20,10 @@ deleted (CLAUDE-CLEANUP §2 / T-1101). ## Frame0 wireframes (`/frame0-wireframe`) **Output taxonomy:** wireframe JSON (source of truth) + exported PNG under -`docs/design/wireframes/{category}/{name}.json` — categories include `insert/` -(neural-insert HUD), `dialogue/`, etc. Palette/style source is +`docs/design/wireframes/{category}/{name}.json` — categories: +`character-creation/`, `dialogue/`, `hud/` (incl. the neural-insert HUD +wireframes `v01-insert-basic.json`, `v10-insert-full.json`), `menus/`, +`monologue/`, `navigator/`, `popups/`. Palette/style source is `docs/design/visual-grammar-v01.md`. **Agent routing:** Araminta (art direction / visual consistency) owns wireframe review; diff --git a/governance/README.md b/governance/README.md index ab9603c2a..f5aec5f82 100644 --- a/governance/README.md +++ b/governance/README.md @@ -462,3 +462,4 @@ line in place — keep the Q-record for the audit trail rather than deleting it. - [R-010: protobuf for client-server serialization](rejected/architecture.md#r-010-protobuf-for-client-server-serialization) — _architecture_ - [R-011: Single currency for Phase 2 economics](rejected/economics.md#r-011-single-currency-for-phase-2-economics) — _economics_ - [R-012: Overheard NPC conversation system (D-078) — scrapped](rejected/perception.md#r-012-overheard-npc-conversation-system-d-078--scrapped) — _perception_ +- [R-013: Localization / i18n](rejected/scope.md#r-013-localization--i18n) — _scope_ diff --git a/governance/rejected/scope.md b/governance/rejected/scope.md index f53ff7729..923083aa4 100644 --- a/governance/rejected/scope.md +++ b/governance/rejected/scope.md @@ -4,4 +4,5 @@ Rejected proposals in the **scope** domain, rationale preserved for the audit tr ### R-013: Localization / i18n - **Rejected:** 2026-07-13 +- **Cross-reference:** D-123 (content.md — Generative AI for NPC content), D-138 (content.md — LLM re-voicing pipeline); roster removal in `.claude/agents/README.md` + `TEAM.md` - **Reason:** Full localization — an i18n framework, translation management, and the volume of template writing the Settled Reach's procedurally-composed text would demand — is prohibitively expensive for a solo developer, and compounds against the Gemma-based naming/text generation: every generated string would need a parallel translation pathway. The game ships English-only. The `tiger` (Translator) agent persona was removed with this decision (see `.claude/agents/README.md` and TEAM.md). Revisit only if project scope and resourcing change fundamentally. diff --git a/tooling/db/common.py b/tooling/db/common.py index 27223c48b..87bcfb463 100644 --- a/tooling/db/common.py +++ b/tooling/db/common.py @@ -75,24 +75,25 @@ def get_base_url(key: str, default: str) -> str: return load_config().get(key, default) -def get_api_key(env_var: str, config_key: str) -> str: - """Get an API key from the environment or config.json. +def get_api_key(env_var: str, config_key: str = "") -> str: + """Get an API key from the environment — environment-only, by design. - Checks the ``env_var`` environment variable first, then ``config_key`` in - config.json. Prints a JSON error and exits 1 if neither is set — connector - scripts emit machine-readable JSON on all paths. + ``tooling/db/config.json`` is a *tracked* file and holds endpoints only; + it must never carry secrets, so there is deliberately no config.json + fallback here (the old one steered users toward committing paid API keys). + ``config_key`` is retained in the signature for caller compatibility but + is ignored. Prints a JSON error and exits 1 if the variable is unset — + connector scripts emit machine-readable JSON on all paths. """ key = os.environ.get(env_var) if key: return key - try: - with open(CONFIG_PATH) as f: - config = json.load(f) - return config.get(config_key, "") - except Exception: - pass print(json.dumps({ "ok": False, - "error": f"No {env_var} found in environment or config.json" + "error": ( + f"{env_var} not set. Export it in your shell or add it to the " + "machine-local .claude/settings.local.json env block (untracked). " + "Never put keys in tooling/db/config.json — it is tracked." + ) }, indent=2)) sys.exit(1) diff --git a/tooling/godot-cold-parse b/tooling/godot-cold-parse index b52c181d3..053069685 100755 --- a/tooling/godot-cold-parse +++ b/tooling/godot-cold-parse @@ -28,15 +28,50 @@ RUN_MENU=false rm -f "$REPO_ROOT/client/.godot/global_script_class_cache.cfg" +# A truly cold checkout (fresh clone or worktree — .godot/ is gitignored) has +# no resource-import cache, and every imported asset (fonts, ogg) then "fails +# loading" during the parse run: a wall of false positives. Seed the cache +# with an import pass first; source assets are tracked, so this is always +# reconstructible. (Found live: first run in a fresh worktree, 2026-07-13.) +if [ ! -d "$REPO_ROOT/client/.godot/imported" ] || [ -z "$(ls -A "$REPO_ROOT/client/.godot/imported" 2>/dev/null)" ]; then + echo "godot-cold-parse: no import cache — running one-time import pass..." >&2 + set +e + IMPORT_OUT=$(godot --headless --path "$REPO_ROOT/client" --import 2>&1) + IMPORT_EXIT=$? + set -e + if [ "$IMPORT_EXIT" -ne 0 ]; then + echo "godot-cold-parse: import pass exited $IMPORT_EXIT" >&2 + printf '%s\n' "$IMPORT_OUT" | tail -20 >&2 + exit "$IMPORT_EXIT" + fi +fi + FILTER='^(SCRIPT )?ERROR|Parse Error|Export type' -MATCHES=$(godot --headless --path "$REPO_ROOT/client" --quit 2>&1 \ +# Capture the godot run separately from the filter pipeline: with the +# trailing `|| true` on the greps, a nonzero exit from godot itself (crash, +# missing binary, corrupted install) would otherwise report "clean". Nothing +# downstream re-reads the raw output now that this is scripted, so fail loud. +set +e +RAW=$(godot --headless --path "$REPO_ROOT/client" --quit 2>&1) +GODOT_EXIT=$? +set -e +if [ "$GODOT_EXIT" -ne 0 ]; then + echo "godot-cold-parse: godot itself exited $GODOT_EXIT — not a parse verdict" >&2 + printf '%s\n' "$RAW" | tail -20 >&2 + exit "$GODOT_EXIT" +fi + +MATCHES=$(printf '%s\n' "$RAW" \ | grep -iE "$FILTER" \ | grep -v "Failed loading resource: res://assets" \ | grep -v "Cannot infer the type" \ | grep -vE '(Messagepack|LocalBridge|ServerProcess|Constants)" not declared' || true) if [ "$RUN_MENU" = true ]; then + # Deliberately no exit-code check here: `timeout` kills the menu after + # 10s by design (exit 124 is the expected shutdown path); only the + # scraped error lines carry signal for this bounded run. MENU_MATCHES=$(timeout 10 godot --path "$REPO_ROOT/client" res://scenes/main_menu.tscn 2>&1 \ | grep -iE "$FILTER" || true) if [ -n "$MENU_MATCHES" ]; then diff --git a/tooling/pr-watchlist-diff b/tooling/pr-watchlist-diff index bcedecbe5..42ac88d56 100755 --- a/tooling/pr-watchlist-diff +++ b/tooling/pr-watchlist-diff @@ -17,7 +17,16 @@ set -euo pipefail BASE="${1:?usage: tooling/pr-watchlist-diff <base> <head>}" HEAD="${2:?usage: tooling/pr-watchlist-diff <base> <head>}" -mapfile -t GENERATOR_SOURCES < <(python3 tooling/generator_sources.py --list) +# Load the registry via plain assignment (set -e sees its failure), not +# `mapfile < <(...)` — a failed process substitution is invisible to set -e +# and would silently yield an empty watch list, disabling the DB-staleness +# net exactly when the shared registry breaks. Guard the empty case too. +SOURCES_RAW="$(python3 tooling/generator_sources.py --list)" +if [ -z "$SOURCES_RAW" ]; then + echo "pr-watchlist-diff: generator_sources.py --list returned nothing" >&2 + exit 1 +fi +mapfile -t GENERATOR_SOURCES <<< "$SOURCES_RAW" git diff --name-only "$BASE...$HEAD" -- \ "${GENERATOR_SOURCES[@]}" \