fix(meta): PR #175 review round — all 14 findings addressed
H1 godot-cold-parse exit-code guard (+bonus: import-pass for cold checkouts, found live); H2 pr-watchlist-diff loud registry failure; H3/T1 deny list :* normalization (add-only) + uniform allow syntax; H4/T7 helper-script allows; H5 get_api_key env-only (config.json is tracked — no secret fallback); H6 TEAM.md active/standby split; H7 troblum solo-profiling note; H8 pr-review frontmatter Task->Agent; H9 conventions doc taxonomies fixed vs real tree; T2 ask-gate leash files (settings/hooks) in tracked settings; T3 R-013 cross-reference; T4 governance README index regenerated (pql decisions sync); T5 dudley briefing ACTIVE; T6 pr-process step 7 run-from-main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -107,7 +107,7 @@ Synthesize findings.
|
|||||||
## Agent usage notes
|
## Agent usage notes
|
||||||
|
|
||||||
### Troblum (Technical Consultant)
|
### 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
|
- Spawned for specific evaluation sidequests, not open-ended discussion
|
||||||
- Blunt, data-driven, will challenge Tyre's assumptions with evidence
|
- Blunt, data-driven, will challenge Tyre's assumptions with evidence
|
||||||
|
|
||||||
|
|||||||
+27
-2
@@ -54,8 +54,10 @@
|
|||||||
|
|
||||||
"Bash(godot4:*)",
|
"Bash(godot4:*)",
|
||||||
"Bash(gdformat:*)",
|
"Bash(gdformat:*)",
|
||||||
"Bash(tooling/atlas *)",
|
"Bash(tooling/atlas:*)",
|
||||||
"Bash(tooling/validate-ron:*)",
|
"Bash(tooling/validate-ron:*)",
|
||||||
|
"Bash(tooling/godot-cold-parse:*)",
|
||||||
|
"Bash(tooling/pr-watchlist-diff:*)",
|
||||||
|
|
||||||
"Bash(chmod *)",
|
"Bash(chmod *)",
|
||||||
"Bash(ls *)",
|
"Bash(ls *)",
|
||||||
@@ -80,9 +82,32 @@
|
|||||||
],
|
],
|
||||||
"deny": [
|
"deny": [
|
||||||
"Bash(git push --force *)",
|
"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 reset --hard)",
|
||||||
"Bash(git clean -f *)",
|
"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": {
|
"hooks": {
|
||||||
|
|||||||
@@ -285,9 +285,13 @@ git diff --stat main...<branch>
|
|||||||
Draft title (`<type>(<scope>): <summary>`, max 70 chars) and description.
|
Draft title (`<type>(<scope>): <summary>`, max 70 chars) and description.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# NB: omit --repo — an explicit slug makes tea skip local-repo setup and
|
# NB — both halves matter (.claude/rules/tea-cli.md):
|
||||||
# pr create dies with "local repository required". Run from the MAIN checkout;
|
# 1. omit --repo: an explicit slug makes tea skip local-repo setup and
|
||||||
# tea infers the repo from origin. (.claude/rules/tea-cli.md)
|
# 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 \
|
tea pr create \
|
||||||
--login schweitz \
|
--login schweitz \
|
||||||
--title "<title>" \
|
--title "<title>" \
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ description: >
|
|||||||
/pr-review. Spawns reviewers matched to the branch type (code, copy, visual,
|
/pr-review. Spawns reviewers matched to the branch type (code, copy, visual,
|
||||||
audio) in parallel. Reports approve/reject with inline comments.
|
audio) in parallel. Reports approve/reject with inline comments.
|
||||||
user-invocable: true
|
user-invocable: true
|
||||||
allowed-tools: Bash, Read, Grep, Glob, Task, Write
|
allowed-tools: Bash, Read, Grep, Glob, Agent, Write
|
||||||
---
|
---
|
||||||
|
|
||||||
# PR Review Skill
|
# PR Review Skill
|
||||||
|
|||||||
@@ -31,16 +31,21 @@
|
|||||||
|-------|------|-------|-------------|
|
|-------|------|-------|-------------|
|
||||||
| **SI** | Project Manager & Scrum Master | Sprint planning, ticket management, turning decisions into executable work. Manages `/ticket` skill. | Always (from now) |
|
| **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)
|
## Standby Team (activate when needed)
|
||||||
|
|
||||||
| Agent | Role | Focus | When active |
|
| 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 |
|
| **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 |
|
| **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
|
## Agent Briefings
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Dudley - Project Briefing"
|
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
|
type: briefing
|
||||||
status: active
|
status: active
|
||||||
agent: Dudley
|
agent: Dudley
|
||||||
@@ -8,14 +8,19 @@ updated: 2026-03-13
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Dudley - Project Briefing
|
# Dudley - Project Briefing
|
||||||
Last updated: 2026-03-13
|
Last updated: 2026-07-13
|
||||||
|
|
||||||
## Current Project State
|
## Current Project State
|
||||||
Check active work: `pql ticket list --status in_progress`
|
Check active work: `pql ticket list --status in_progress`
|
||||||
Check your tickets: `pql ticket list --assigned dudley`
|
Check your tickets: `pql ticket list --assigned dudley`
|
||||||
|
|
||||||
## Status
|
## 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
|
## Key Decisions for Your Domain
|
||||||
Read `decisions/architecture.md` (primary) and `decisions/content.md` (NPC model). Key decisions:
|
Read `decisions/architecture.md` (primary) and `decisions/content.md` (NPC model). Key decisions:
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ deleted (CLAUDE-CLEANUP §2 / T-1101).
|
|||||||
## d2 diagrams (`/d2-diagram`)
|
## d2 diagrams (`/d2-diagram`)
|
||||||
|
|
||||||
**Output taxonomy:** `.d2` source + `.png`/`.svg` renders under
|
**Output taxonomy:** `.d2` source + `.png`/`.svg` renders under
|
||||||
`docs/diagrams/{category}/{name}.d2` — categories in use: `architecture/`, `ui/`,
|
`docs/diagrams/{category}/{name}.d2` — categories in use: `architecture/`,
|
||||||
`entity/`. Create/update a diagram whenever a D-record is added or modified.
|
`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):
|
**Agent routing** (who to task with a diagram, by domain):
|
||||||
- **Qatux** — architecture decision records, system-overview + data-flow diagrams.
|
- **Qatux** — architecture decision records, system-overview + data-flow diagrams.
|
||||||
@@ -19,8 +20,10 @@ deleted (CLAUDE-CLEANUP §2 / T-1101).
|
|||||||
## Frame0 wireframes (`/frame0-wireframe`)
|
## Frame0 wireframes (`/frame0-wireframe`)
|
||||||
|
|
||||||
**Output taxonomy:** wireframe JSON (source of truth) + exported PNG under
|
**Output taxonomy:** wireframe JSON (source of truth) + exported PNG under
|
||||||
`docs/design/wireframes/{category}/{name}.json` — categories include `insert/`
|
`docs/design/wireframes/{category}/{name}.json` — categories:
|
||||||
(neural-insert HUD), `dialogue/`, etc. Palette/style source is
|
`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`.
|
`docs/design/visual-grammar-v01.md`.
|
||||||
|
|
||||||
**Agent routing:** Araminta (art direction / visual consistency) owns wireframe review;
|
**Agent routing:** Araminta (art direction / visual consistency) owns wireframe review;
|
||||||
|
|||||||
@@ -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-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-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-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_
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ Rejected proposals in the **scope** domain, rationale preserved for the audit tr
|
|||||||
|
|
||||||
### R-013: Localization / i18n
|
### R-013: Localization / i18n
|
||||||
- **Rejected:** 2026-07-13
|
- **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.
|
- **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.
|
||||||
|
|||||||
+13
-12
@@ -75,24 +75,25 @@ def get_base_url(key: str, default: str) -> str:
|
|||||||
return load_config().get(key, default)
|
return load_config().get(key, default)
|
||||||
|
|
||||||
|
|
||||||
def get_api_key(env_var: str, config_key: str) -> str:
|
def get_api_key(env_var: str, config_key: str = "") -> str:
|
||||||
"""Get an API key from the environment or config.json.
|
"""Get an API key from the environment — environment-only, by design.
|
||||||
|
|
||||||
Checks the ``env_var`` environment variable first, then ``config_key`` in
|
``tooling/db/config.json`` is a *tracked* file and holds endpoints only;
|
||||||
config.json. Prints a JSON error and exits 1 if neither is set — connector
|
it must never carry secrets, so there is deliberately no config.json
|
||||||
scripts emit machine-readable JSON on all paths.
|
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)
|
key = os.environ.get(env_var)
|
||||||
if key:
|
if key:
|
||||||
return 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({
|
print(json.dumps({
|
||||||
"ok": False,
|
"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))
|
}, indent=2))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -28,15 +28,50 @@ RUN_MENU=false
|
|||||||
|
|
||||||
rm -f "$REPO_ROOT/client/.godot/global_script_class_cache.cfg"
|
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'
|
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 -iE "$FILTER" \
|
||||||
| grep -v "Failed loading resource: res://assets" \
|
| grep -v "Failed loading resource: res://assets" \
|
||||||
| grep -v "Cannot infer the type" \
|
| grep -v "Cannot infer the type" \
|
||||||
| grep -vE '(Messagepack|LocalBridge|ServerProcess|Constants)" not declared' || true)
|
| grep -vE '(Messagepack|LocalBridge|ServerProcess|Constants)" not declared' || true)
|
||||||
|
|
||||||
if [ "$RUN_MENU" = true ]; then
|
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 \
|
MENU_MATCHES=$(timeout 10 godot --path "$REPO_ROOT/client" res://scenes/main_menu.tscn 2>&1 \
|
||||||
| grep -iE "$FILTER" || true)
|
| grep -iE "$FILTER" || true)
|
||||||
if [ -n "$MENU_MATCHES" ]; then
|
if [ -n "$MENU_MATCHES" ]; then
|
||||||
|
|||||||
@@ -17,7 +17,16 @@ set -euo pipefail
|
|||||||
BASE="${1:?usage: tooling/pr-watchlist-diff <base> <head>}"
|
BASE="${1:?usage: tooling/pr-watchlist-diff <base> <head>}"
|
||||||
HEAD="${2:?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" -- \
|
git diff --name-only "$BASE...$HEAD" -- \
|
||||||
"${GENERATOR_SOURCES[@]}" \
|
"${GENERATOR_SOURCES[@]}" \
|
||||||
|
|||||||
Reference in New Issue
Block a user