docs: nested CLAUDE.md via init-deep
Slim root CLAUDE.md (90 lines) and add 7 nested CLAUDE.md files for progressive disclosure. Each nested file loads automatically when Claude reads files in that directory. Nested files: - game/ scenarios + NPC phrases (source-of-truth) - tools/ Python tooling (Runtime 3, TTS, MCP, dev CLI) - tests/ Python unittest patterns - specs/ contracts governance - frontend-scratch-v2/ React 19 + Blockly + Vite (legacy) - frontend-v3/ pnpm monorepo (production target) - desktop/ Electron Zacus Studio (macOS) Root CLAUDE.md gains a Where-to-Look table and pointer to nested guidance. ESP32_ZACUS submodule is intentionally not covered (separate repo with its own CI).
This commit is contained in:
@@ -1,93 +1,90 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
Guidance for Claude Code working in this repository.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
**Le Mystère du Professeur Zacus** — hybrid educational escape room game with ESP32-S3 hardware, React authoring studio, and a portable Runtime 3 scenario engine. Scenarios are authored in YAML, compiled to a portable IR, and executed on both web and ESP32 targets.
|
**Le Mystère du Professeur Zacus** — hybrid educational escape room game with ESP32-S3 hardware, React authoring studios, and a portable Runtime 3 scenario engine. Scenarios are authored in YAML, compiled to a portable IR, and executed on both web and ESP32 targets.
|
||||||
|
|
||||||
## Build & Test
|
## Build & Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Full validation pipeline
|
make all-validate # Full pipeline (scenario + audio + printables + runtime3)
|
||||||
make all-validate
|
make content-checks # Schema validation only
|
||||||
|
|
||||||
# Content checks (schema validation for scenarios, audio, printables)
|
# Runtime 3
|
||||||
make content-checks
|
make runtime3-compile # YAML → IR
|
||||||
|
make runtime3-simulate # Execute IR
|
||||||
# Runtime 3 — compile, simulate, verify, test
|
make runtime3-verify # Verify pivots
|
||||||
make runtime3-compile # YAML → Runtime 3 IR
|
make runtime3-test # Python unittest
|
||||||
make runtime3-simulate # simulate scenario execution
|
|
||||||
make runtime3-verify # verify pivot logic
|
|
||||||
make runtime3-test # Python unittest suite
|
|
||||||
|
|
||||||
# Frontend (React 19 + Blockly + Vite)
|
|
||||||
cd frontend-scratch-v2
|
|
||||||
npm test # Vitest (18 tests)
|
|
||||||
npm run build # tsc -b + vite build
|
|
||||||
npx vitest run tests/specific.test.ts # single test
|
|
||||||
|
|
||||||
# Compile a specific scenario
|
# Compile a specific scenario
|
||||||
python3 tools/scenario/compile_runtime3.py game/scenarios/zacus_v2.yaml
|
python3 tools/scenario/compile_runtime3.py game/scenarios/zacus_v2.yaml
|
||||||
|
|
||||||
# Dev CLI (12 actions)
|
# Dev CLI (12 actions)
|
||||||
./tools/dev/zacus.sh content-checks
|
./tools/dev/zacus.sh content-checks
|
||||||
./tools/dev/zacus.sh runtime3-compile
|
|
||||||
./tools/dev/zacus.sh voice-bridge start|stop|status|test
|
./tools/dev/zacus.sh voice-bridge start|stop|status|test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Frontend, desktop, and tooling commands live in their nested CLAUDE.md.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Data Flow
|
|
||||||
```
|
```
|
||||||
YAML scenario → compile_runtime3.py → Runtime 3 IR → ESP32 / Web player
|
YAML scenario → compile_runtime3.py → Runtime 3 IR → ESP32 / Web player
|
||||||
↓
|
↓
|
||||||
Voice bridge → Piper TTS (Tower:8001)
|
Voice bridge → Piper TTS (Tower:8001)
|
||||||
↓
|
↓
|
||||||
Hints engine → /hints/ask (3 puzzles, 3 levels)
|
Hints engine → /hints/ask
|
||||||
```
|
```
|
||||||
|
|
||||||
### Key Components
|
Key surfaces:
|
||||||
|
- **Scenario IR**: `game/scenarios/zacus_v2.yaml` → `tools/scenario/compile_runtime3.py` → portable Runtime 3 IR. Contract: `specs/ZACUS_RUNTIME_3_SPEC.md`.
|
||||||
|
- **Authoring**: `frontend-scratch-v2/` (legacy single-app) and `frontend-v3/` (pnpm monorepo, dashboard + editor + simulation).
|
||||||
|
- **Firmware**: `ESP32_ZACUS/` submodule (separate repo, separate CI). Freenove ESP32-S3 + PlatformIO. NPC engine, voice pipeline, vision/QR, media manager.
|
||||||
|
- **Voice / NPC**: Piper TTS on Tower:8001 (zacus voice = tom-medium). NPC phrases in `game/scenarios/npc_phrases.yaml`. MP3 pool generator: `tools/tts/generate_npc_pool.py`.
|
||||||
|
- **MCP hardware**: `tools/dev/mcp_hardware_server.py` (stdio, 6 tools).
|
||||||
|
- **Desktop hub**: `desktop/` (Electron, macOS, bundles V3 frontends, talks USB serial).
|
||||||
|
|
||||||
- **Scenario engine**: `game/scenarios/zacus_v2.yaml` is the canonical source. Runtime 3 spec in `specs/ZACUS_RUNTIME_3_SPEC.md`.
|
## Where to Look
|
||||||
- **Frontend studio** (`frontend-scratch-v2/`): React 19 + Blockly 12.4 + Monaco Editor + Zod validation. Vite bundler, TypeScript.
|
|
||||||
- **ESP32 firmware** (`ESP32_ZACUS/` submodule): Freenove ESP32-S3, PlatformIO build. Separate repo with its own CI. Contains voice pipeline scaffold, audio/vision/QR detection, media manager.
|
|
||||||
- **Runtime 3 compiler** (`tools/scenario/compile_runtime3.py`): YAML → portable IR with pivots, zones, triggers.
|
|
||||||
- **Voice pipeline**: Piper TTS on Tower:8001 (3 voices: zacus=tom-medium, siwis, upmc), ESP-SR for wake word. Voice bridge routes `[HINT:puzzle:level]` to hints engine.
|
|
||||||
- **MCP hardware server** (`tools/dev/mcp_hardware_server.py`): 6 tools, stdio transport for hardware interaction.
|
|
||||||
- **TUI dashboard** (`tools/dev/zacus_tui.py`): 12 actions, logs, CI mode.
|
|
||||||
- **Analytics**: ESP32 module + 6 web endpoints + Dashboard UI.
|
|
||||||
- **NPC Engine** (`ESP32_ZACUS/ui_freenove_allinone/include/npc/npc_engine.h` + `src/npc/npc_engine.cpp`): Lightweight C state machine for Professor Zacus NPC. Trigger rules (stuck timer, QR scan, fast/slow progress, hint request), mood system (neutral/impressed/worried/amused), hybrid audio routing (live Piper TTS when Tower reachable, SD card MP3 fallback). NPC phrase bank in `game/scenarios/npc_phrases.yaml`.
|
|
||||||
- **TTS Client** (`ESP32_ZACUS/ui_freenove_allinone/include/npc/tts_client.h` + `src/npc/tts_client.cpp`): HTTP client for Piper TTS on Tower:8001 with health-check, PSRAM WAV buffer, and SD card fallback. Voice: tom-medium.
|
|
||||||
- **NPC Phrase Bank** (`game/scenarios/npc_phrases.yaml`): All Professor Zacus lines in French, organized by category: hints (3 levels × 6 scenes), congratulations, warnings, personality comments (by mood), adaptation phrases (skip/challenge/timer), narrative bridges, false leads, and ambiance (intro/outro/idle).
|
|
||||||
- **NPC MP3 Pool Generator** (`tools/tts/generate_npc_pool.py`): Python tool that reads `npc_phrases.yaml`, calls Piper TTS API for each phrase, writes MP3 files to `hotline_tts/`, and generates `hotline_tts/manifest.json`. Idempotent (skips already-generated files). Run: `python3 tools/tts/generate_npc_pool.py [--dry-run]`.
|
|
||||||
|
|
||||||
### AI Integration
|
| Task | Location |
|
||||||
- 6 AI agent definitions in `.github/agents/` (voice, tts, vision, hints, audio_gen, mcp)
|
|------|----------|
|
||||||
- Hints engine: anti-cheat, 3 difficulty levels, per-puzzle context
|
| Edit scenarios, NPC phrases, prompts | `game/` |
|
||||||
- Spec: `specs/AI_INTEGRATION_SPEC.md`
|
| Modify Runtime 3 compiler / validators / TTS pool / dev CLI | `tools/` |
|
||||||
|
| Authoring UI (legacy) | `frontend-scratch-v2/` |
|
||||||
|
| Authoring UI / dashboard / simulation (current) | `frontend-v3/` |
|
||||||
|
| Zacus Studio macOS app | `desktop/` |
|
||||||
|
| Add or change a contract spec | `specs/` |
|
||||||
|
| Python tests (Runtime 3, NPC) | `tests/` |
|
||||||
|
| Firmware code | `ESP32_ZACUS/` submodule (own repo) |
|
||||||
|
|
||||||
## Canonical Files
|
## Canonical Files
|
||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `game/scenarios/zacus_v2.yaml` | Scenario source of truth (v3, Runtime 3) |
|
| `game/scenarios/zacus_v2.yaml` | Scenario source of truth |
|
||||||
| `game/scenarios/npc_phrases.yaml` | Professor Zacus NPC phrase bank (all categories, French) |
|
| `game/scenarios/npc_phrases.yaml` | NPC phrase bank (FR) |
|
||||||
| `tools/tts/generate_npc_pool.py` | NPC MP3 pool generator (Piper TTS → hotline_tts/) |
|
| `specs/ZACUS_RUNTIME_3_SPEC.md` | Runtime contract |
|
||||||
| `specs/ZACUS_RUNTIME_3_SPEC.md` | Runtime contract definition |
|
|
||||||
| `specs/AI_INTEGRATION_SPEC.md` | AI layer architecture |
|
| `specs/AI_INTEGRATION_SPEC.md` | AI layer architecture |
|
||||||
| `Makefile` | Main automation entry point |
|
| `Makefile` | Top-level automation |
|
||||||
| `docs/QUICKSTART.md` | Getting started |
|
| `docs/QUICKSTART.md` | Getting started |
|
||||||
| `docs/DEPLOYMENT_RUNBOOK.md` | Field deployment |
|
| `docs/DEPLOYMENT_RUNBOOK.md` | Field deployment |
|
||||||
| `docs/debt/codex-firmware-fixes-to-apply.md` | Pending firmware fixes (I2S, CORS, AP password) |
|
| `docs/debt/codex-firmware-fixes-to-apply.md` | Pending firmware fixes |
|
||||||
|
|
||||||
## Language & Communication
|
|
||||||
|
|
||||||
- User speaks **French**, code and docs in **English**
|
|
||||||
- Respond in French for conversation, English for code/comments/commits
|
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
|
|
||||||
- **Tower** (`clems@192.168.0.120`): Piper TTS FR on port 8001
|
- **Tower** (`clems@192.168.0.120`): Piper TTS FR on `:8001` (voices: tom-medium / siwis / upmc).
|
||||||
- **KXKM-AI** (`kxkm@kxkm-ai`): RTX 4090, GPU inference
|
- **KXKM-AI** (`kxkm@kxkm-ai` via Tailscale): RTX 4090, GPU inference.
|
||||||
- SSH is key-based only, never use sshpass
|
- SSH is key-based only — never `sshpass`.
|
||||||
|
|
||||||
|
## Language
|
||||||
|
|
||||||
|
User speaks French → respond in French. Code, comments, commits, docs → English. Full diacritics required in French.
|
||||||
|
|
||||||
|
## Nested Guidance
|
||||||
|
|
||||||
|
Domain-specific rules live in nested `CLAUDE.md` files and load automatically when you read files in those directories. Closest file wins. Current nested:
|
||||||
|
|
||||||
|
- `game/`, `tools/`, `tests/`, `specs/`
|
||||||
|
- `frontend-scratch-v2/`, `frontend-v3/`, `desktop/`
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Desktop — Zacus Studio
|
||||||
|
|
||||||
|
macOS Electron control hub for Professeur Zacus escape room kits. Bundles the V3 frontends, talks to ESP32 over USB serial, and ships notarised universal/arm64 builds.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
main/ # Electron main process (Node) — IPC, serial, app lifecycle
|
||||||
|
preload/ # Context-bridge preload (exposes safe APIs to renderer)
|
||||||
|
renderer/ # React renderer (UI)
|
||||||
|
scripts/
|
||||||
|
build-frontends.sh # Bundles frontend-v3 apps into resources/
|
||||||
|
notarize.js # Apple notarization (post-build hook)
|
||||||
|
resources/ # Bundled frontend artefacts + assets
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install # Triggers electron-builder install-app-deps + electron-rebuild
|
||||||
|
npm run dev # Concurrent main + renderer
|
||||||
|
npm run build # tsc main + vite renderer
|
||||||
|
npm run build:mac # Universal (x64 + arm64) installer
|
||||||
|
npm run build:mac-arm64 # Apple Silicon only
|
||||||
|
npm run rebuild-native # zacus-native addon for current Electron ABI
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
- Three tsconfigs (`tsconfig.main.json`, `tsconfig.renderer.json`, root) — never share between processes; main and renderer have different module systems.
|
||||||
|
- Serial port access lives in `src/main/` only. Renderer talks via `preload` IPC, never imports `serialport`.
|
||||||
|
- Native modules (`serialport`, `zacus-native`) need rebuilding for Electron's Node ABI — run `npm run rebuild-native` after Electron upgrades.
|
||||||
|
- Notarization requires `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID` env vars; never commit them.
|
||||||
|
|
||||||
|
## Frontend Bundle
|
||||||
|
|
||||||
|
`scripts/build-frontends.sh` builds `frontend-v3/` apps and copies them into `resources/`. Run it before `electron-builder` if frontend changed.
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Calling `require('serialport')` from renderer (security: nodeIntegration must stay false)
|
||||||
|
- Hardcoding device paths (`/dev/cu.usbserial-*`) — enumerate at runtime via `serialport.list()`
|
||||||
|
- Skipping notarization on release builds (Gatekeeper will block)
|
||||||
|
- Bundling V2 frontend instead of V3 — V2 is dev-only
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Frontend Scratch V2
|
||||||
|
|
||||||
|
Authoring studio: React 19 + Blockly 12 + Monaco Editor + Zod + Vite 7 + Vitest 3. TypeScript strict.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
App.tsx, main.tsx # Entry
|
||||||
|
components/ # UI (scenario editor, blocks, panels)
|
||||||
|
lib/
|
||||||
|
api.ts # ESP32/runtime HTTP client
|
||||||
|
runtime3.ts # Runtime 3 IR types + compiler bindings (TS mirror of Python)
|
||||||
|
scenario.ts # Scenario YAML/JSON helpers
|
||||||
|
useRuntimeStore.ts # Zustand-style runtime state hook
|
||||||
|
__tests__/ # Co-located unit tests
|
||||||
|
tests/
|
||||||
|
scenario-runtime3.test.ts # Integration test against real fixtures
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev # Vite dev server
|
||||||
|
npm run build # tsc -b && vite build
|
||||||
|
npm test # Vitest run (18 tests)
|
||||||
|
npm run lint # ESLint flat config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
- `lib/runtime3.ts` is the TS mirror of Python `tools/scenario/runtime3_common.py` — keep field names + optionality identical. When the Python IR changes, update both in the same PR.
|
||||||
|
- Validate scenario imports through Zod schemas, not raw `JSON.parse` — Zod errors surface bad fixtures during dev.
|
||||||
|
- Blockly toolbox/blocks live in `components/scenario-editor/` — register custom blocks before `Blockly.inject`.
|
||||||
|
- Monaco editor language is YAML; load worker via Vite import, never CDN.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
- Vitest config inherits from `vite.config.ts`. Use `vitest run --reporter verbose` for diff output.
|
||||||
|
- Cross-stack invariant tests (Python compile → TS replay) live in `tests/scenario-runtime3.test.ts`. Run them locally before claiming Runtime 3 work is done.
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Drifting `lib/runtime3.ts` types from the Python compiler — schedule a sync, don't bandaid
|
||||||
|
- Calling `fetch()` outside `lib/api.ts` — keep network code centralised
|
||||||
|
- Using `any` to escape Zod typing — narrow with `z.infer` instead
|
||||||
|
- Importing from `frontend-v3/` packages — V2 and V3 share no runtime code
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Frontend V3 (Monorepo)
|
||||||
|
|
||||||
|
pnpm + turbo monorepo. Three apps + three shared packages. Node ≥20, pnpm ≥9.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/
|
||||||
|
dashboard/ # Live game-master dashboard (analytics, control)
|
||||||
|
editor/ # Scenario editor (V3 successor of frontend-scratch-v2)
|
||||||
|
simulation/ # Runtime simulator / playtest UI
|
||||||
|
packages/
|
||||||
|
scenario-engine/ # Runtime 3 IR + execution (shared core)
|
||||||
|
shared/ # Cross-app utilities, types, constants
|
||||||
|
ui/ # Shared component library
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install # Bootstrap workspace
|
||||||
|
pnpm dev # turbo run dev (all apps)
|
||||||
|
pnpm --filter dashboard dev # Single app
|
||||||
|
pnpm build # turbo run build
|
||||||
|
pnpm test # turbo run test
|
||||||
|
pnpm typecheck # turbo run typecheck
|
||||||
|
pnpm lint # turbo run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
- `scenario-engine` is the canonical TS Runtime 3 implementation. New apps consume it; do not fork the IR.
|
||||||
|
- All cross-app types live in `packages/shared` — never copy-paste types between apps.
|
||||||
|
- Components shared across ≥2 apps move to `packages/ui`. Keep app-local components in `apps/<app>/src/components`.
|
||||||
|
- Turbo cache keys: ensure `inputs` in `turbo.json` cover all source paths; missing globs cause stale cache.
|
||||||
|
|
||||||
|
## V2 vs V3
|
||||||
|
|
||||||
|
- `frontend-scratch-v2/` is the legacy single-app Vite playground. V3 is the production target.
|
||||||
|
- When porting features from V2, extract reusable logic into `packages/scenario-engine` or `packages/shared` first.
|
||||||
|
- Do not symlink or import across V2/V3 boundaries.
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Adding deps to root `package.json` (use the relevant app/package)
|
||||||
|
- Bypassing turbo with manual `tsc` invocations — breaks cache invalidation
|
||||||
|
- Circular dependencies between packages (turbo will warn; fix immediately)
|
||||||
|
- Publishing `packages/*` to npm — they're internal workspace-only
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Game Content
|
||||||
|
|
||||||
|
Source-of-truth for scenarios, NPC dialogue, and prompts. Edits here propagate to firmware, frontends, and TTS pools.
|
||||||
|
|
||||||
|
## Canonical Files
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
|------|------|
|
||||||
|
| `scenarios/zacus_v2.yaml` | Master scenario (Runtime 3 IR source) |
|
||||||
|
| `scenarios/npc_phrases.yaml` | All Professor Zacus lines (FR), categorised |
|
||||||
|
| `prompts/` | LLM prompt templates for hints/coherence |
|
||||||
|
|
||||||
|
## Editing Rules
|
||||||
|
|
||||||
|
- IDs are foreign keys: changing `puzzle.id` or `zone.id` breaks audio + printables manifests, firmware bundles, and frontend tests. Sync cross-file when renaming.
|
||||||
|
- Never edit `exports/` or generated MD — regenerate via `tools/scenario/export_md.py`.
|
||||||
|
- French dialogue only in `npc_phrases.yaml`; full diacritics required.
|
||||||
|
- New NPC phrase categories must be added to schema validator and `tools/tts/generate_npc_pool.py`.
|
||||||
|
|
||||||
|
## Validate Before Commit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/scenario/validate_scenario.py scenarios/zacus_v2.yaml
|
||||||
|
python3 tools/scenario/compile_runtime3.py scenarios/zacus_v2.yaml
|
||||||
|
python3 tools/audio/validate_manifest.py ../audio/manifests/zacus_v2_audio.yaml
|
||||||
|
python3 tools/printables/validate_manifest.py ../printables/manifests/zacus_v2_printables.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Editing generated outputs in `exports/` instead of YAML source
|
||||||
|
- Renaming IDs without grepping the repo for cross-references
|
||||||
|
- Adding NPC phrases without regenerating the MP3 pool
|
||||||
|
- Mixing French narrative with English code identifiers in YAML values
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Specs
|
||||||
|
|
||||||
|
Contracts between firmware, frontends, tooling, and AI agents. Treat each spec as a versioned interface — a breaking change here triggers work in 2+ subsystems.
|
||||||
|
|
||||||
|
## Canonical Specs
|
||||||
|
|
||||||
|
| Spec | Governs |
|
||||||
|
|------|---------|
|
||||||
|
| `ZACUS_RUNTIME_3_SPEC.md` | Scenario IR + execution semantics |
|
||||||
|
| `AI_INTEGRATION_SPEC.md` | Agent layer (hints, voice, vision, TTS, audio_gen, MCP) |
|
||||||
|
| `FIRMWARE_WEB_DATA_CONTRACT.md` | ESP32 ↔ frontend HTTP/WebSocket payloads |
|
||||||
|
| `MCP_HARDWARE_SERVER_SPEC.md` | MCP stdio tools (6) for hardware interaction |
|
||||||
|
| `MEDIA_MANAGER_*` | Media manager (firmware/frontend/runtime/sync) |
|
||||||
|
| `STORY_DESIGNER_SCRATCH_LIKE_SPEC.md` | Frontend Blockly-like authoring |
|
||||||
|
| `STORY_RUNTIME_API_JSON_CONTRACT.md` | Runtime API JSON shape |
|
||||||
|
| `LOCAL_AI_STUDIO_SPEC.md` | Local AI studio integration |
|
||||||
|
| `QA_TEST_MATRIX_SPEC.md` | QA matrix + acceptance criteria |
|
||||||
|
|
||||||
|
## Editing Rules
|
||||||
|
|
||||||
|
- A spec change is a contract change. List affected consumers in the PR description.
|
||||||
|
- Bump the version header at the top of the file when altering existing fields; never silently rename keys.
|
||||||
|
- New spec docs must be linked from root `CLAUDE.md` "Canonical Files" if they introduce a new contract.
|
||||||
|
- Pair runtime/frontend spec changes — the matching `tools/scenario/compile_runtime3.py` and `frontend-scratch-v2/src/lib/runtime3.ts` updates ship in the same PR.
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- "Implementation detail" comments in specs — if it's not part of the contract, remove it
|
||||||
|
- Adding fields without specifying optionality, defaults, and version
|
||||||
|
- Letting the IR drift between Python compiler and TS runtime without updating the spec first
|
||||||
|
- Documenting hardware pins/ports here — they belong in `ESP32_ZACUS/` firmware sources
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
Python unittest suite covering Runtime 3 IR, NPC engine integration, and scenario invariants.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| File / Dir | Scope |
|
||||||
|
|------------|-------|
|
||||||
|
| `test_npc_engine.py` | NPC state machine (triggers, mood, rule firing) |
|
||||||
|
| `test_npc_integration.py` | NPC ↔ TTS ↔ phrase bank wiring |
|
||||||
|
| `test_npc_phrases_schema.py` | `npc_phrases.yaml` schema invariants |
|
||||||
|
| `runtime3/` | Runtime 3 compiler/IR/pivot tests |
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m unittest discover -s tests # full suite
|
||||||
|
python3 -m unittest tests.test_npc_engine # single module
|
||||||
|
make runtime3-test # via Makefile target
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
- Use `unittest.TestCase`, not pytest fixtures (suite is stdlib-only).
|
||||||
|
- Load YAML fixtures from `game/scenarios/`, never inline them — tests must catch drift in real content.
|
||||||
|
- For NPC trigger timing tests, inject a fake clock; never rely on `time.sleep`.
|
||||||
|
- Snapshot the compiled Runtime 3 IR in `runtime3/` tests — diff failures should point at the offending pivot/zone ID.
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Mocking `compile_runtime3.py` output instead of running it on real fixtures
|
||||||
|
- Tests that pass when YAML IDs are renamed but downstream consumers break
|
||||||
|
- Importing from `tools/` without going through public functions
|
||||||
|
- Suppressing UnicodeDecodeError on French content — fix the encoding instead
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Tools
|
||||||
|
|
||||||
|
Python tooling for scenario compilation, validation, voice pipeline, MCP hardware, and dev workflow.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| Subdir | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `scenario/` | Runtime 3 compiler, validator, simulator, pivot verifier, MD/firmware exporters |
|
||||||
|
| `audio/` | Audio manifest validator |
|
||||||
|
| `printables/` | Printables manifest validator |
|
||||||
|
| `tts/` | NPC phrase pool generator (Piper TTS → `hotline_tts/`) |
|
||||||
|
| `stt/` | Speech-to-text helpers |
|
||||||
|
| `dev/` | TUI dashboard, MCP hardware server, `zacus.sh` CLI (12 actions) |
|
||||||
|
| `playtest/` | Playtest harness |
|
||||||
|
| `setup/`, `repo_state/`, `requirements/`, `images/`, `test/` | Auxiliary |
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Every script accepts `--help` (argparse). Run it before invoking with new args.
|
||||||
|
- Idempotent by default — generators (e.g. `tts/generate_npc_pool.py`) skip already-produced artefacts. Add `--force` flags rather than breaking idempotency.
|
||||||
|
- Scripts run from repo root; use repo-relative paths via `pathlib.Path(__file__).resolve().parents[N]`.
|
||||||
|
- All scenario tools share `scenario/runtime3_common.py` for IR types — extend it, don't redefine types per script.
|
||||||
|
|
||||||
|
## Validate Changes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m py_compile <changed_files>
|
||||||
|
python3 tools/scenario/<changed_script>.py --help
|
||||||
|
make all-validate # if scenario tools changed
|
||||||
|
python3 -m unittest discover -s tests # if NPC/runtime touched
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Hardcoding machine-specific paths/ports (Tower IP `192.168.0.120` is acceptable but should be overridable via env)
|
||||||
|
- Requiring chat interaction when a local prompt/script wait suffices
|
||||||
|
- Diverging IR shape between `compile_runtime3.py` and `runtime3_common.py`
|
||||||
|
- Adding new validators without wiring them into `Makefile` `all-validate`
|
||||||
|
- Writing to `game/scenarios/exports/` directly — only `export_md.py` does that
|
||||||
Reference in New Issue
Block a user