Consolidates all connector scripts under tooling/ per project structure conventions. Symlink at db/connectors → tooling/db/ preserves backwards compatibility (remove after Sprint 22). Updated references in CLAUDE.md, Makefile, DEVOPS.md, all skill files, agent files, rules, schema comments, and Sprint 21 briefings. Python scripts updated with correct SCHEMA_PATH (now relative to WORKTREE_ROOT/db/schema.sql). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
307 lines
12 KiB
Markdown
307 lines
12 KiB
Markdown
# DevOps Procedures
|
|
|
|
Operational procedures for building, testing, and running The Settled Reach.
|
|
|
|
## Repository Layout
|
|
|
|
```
|
|
client/ Godot 4 client (GDScript, scenes, assets)
|
|
server/ Rust/bevy_ecs simulation server
|
|
tooling/ Build tools, scripts, asset pipelines
|
|
tests/ Integration and end-to-end tests (cross-boundary)
|
|
decisions/ Decision domain files (source of truth for all D/Q/R entries)
|
|
.config/ Configuration files (linters, formatters, CI)
|
|
.cache/ Local caches for testing/linting (gitignored)
|
|
docs/ Design, architecture, briefings, workshops
|
|
db/ SQLite ticketing + decisions database and connectors
|
|
```
|
|
|
|
Unit tests live inside their respective projects (`server/` uses `#[cfg(test)]` inline + `tests/` directory per D-030). The top-level `tests/` directory is for integration tests that cross the client-server boundary (IPC round-trip, serialization fixtures, divergence tests).
|
|
|
|
## Prerequisites
|
|
|
|
| Tool | Version | Purpose |
|
|
|------|---------|---------|
|
|
| Rust (via rustup) | stable | Server compilation, clippy, rustfmt (auto-installed by `make setup`) |
|
|
| Godot | 4.x | Client editor and runtime (auto-installed to `~/bin/` by `make setup`) |
|
|
| Python | 3.x | Tooling scripts, db connectors |
|
|
| Make | any | Task runner (see below) |
|
|
| curl | any | Downloading Godot |
|
|
| unzip | any | Extracting Godot |
|
|
|
|
## Makefile Targets
|
|
|
|
All development operations go through the top-level `Makefile`. Run `make` with no arguments for a summary.
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
make setup # Install/verify all dev dependencies
|
|
GODOT_VERSION=4.4 make setup # Pin a specific Godot version
|
|
```
|
|
|
|
Downloads and installs Godot to `~/bin/godot4`, installs Rust clippy + rustfmt, and verifies Python/curl/unzip. Skips the download if the correct version is already installed. The `GODOT_VERSION` variable defaults to `4.6` and can be overridden.
|
|
|
|
### Build
|
|
|
|
```bash
|
|
make build # Build both client and server
|
|
make build-server # cargo build in server/
|
|
make build-client # Client builds are editor-managed (prints guidance)
|
|
```
|
|
|
|
### Run
|
|
|
|
```bash
|
|
make server # cargo run in server/
|
|
make client # Launch Godot with client/ project
|
|
```
|
|
|
|
The server must be running before the client connects (subprocess launch will be automated later per D-020).
|
|
|
|
### Test
|
|
|
|
```bash
|
|
make test # Run all tests (test-server + test-client)
|
|
make test-server # Rust tests via tests/run-rust (cargo nextest, JSON summary)
|
|
make test-client # Godot tests via tests/run-godot (gdUnit4 headless, JSON summary)
|
|
```
|
|
|
|
The IPC test layers (D-030) have dedicated targets:
|
|
|
|
```bash
|
|
make test-ipc-fixtures # Layer 1: serialization round-trip fixtures
|
|
make test-ipc-protocol # Layer 2: mock LocalBridge protocol tests
|
|
make test-ipc-integration # Layer 3: real subprocess round-trip (+ benchmark when ready)
|
|
make test-ipc-benchmark # IPC latency benchmark (blocked: #555/#556 handshake)
|
|
```
|
|
|
|
Each `tests/run-*` script outputs a JSON summary to stdout and streams progress to stderr:
|
|
```json
|
|
{"suite":"rust","total":42,"passed":42,"failed":0,"duration_ms":1230}
|
|
```
|
|
|
|
All scripts accept `--filter <name>` to run a subset of tests. They are whitelistable for agent use (no TTY prompts, no interactive input).
|
|
|
|
Server tests use Rust's built-in test framework with `#[cfg(test)]` inline tests and `tests/` integration tests (D-030). Client tests use gdUnit4 (D-030).
|
|
|
|
### Cross-Encoder Fixtures
|
|
|
|
```bash
|
|
make fixtures # Regenerate Rust->GDScript fixtures (server/tests/gen_fixtures.rs)
|
|
make fixtures-client # Generate GDScript->Rust fixtures + verify Rust decoder (#475)
|
|
```
|
|
|
|
The bidirectional protocol is validated by two sets of committed fixtures:
|
|
- **Rust encodes, GDScript decodes:** `client/tests/fixtures/msgpack/` (generated by `make fixtures`)
|
|
- **GDScript encodes, Rust decodes:** `server/tests/fixtures/gdscript/` (generated by `make fixtures-client`)
|
|
|
|
Regenerate both after any protocol change. Commit the updated fixtures alongside the code change.
|
|
|
|
**Troubleshooting fixture failures:**
|
|
|
|
- **`make fixtures-client` fails with encode errors:** Check that `client/addons/messagepack/messagepack.gd` is up to date. The script exits non-zero on any encode failure.
|
|
- **`gdscript_generated_fixtures_deserialize` fails:** Fixtures in `server/tests/fixtures/gdscript/` are stale or corrupted. Re-run `make fixtures-client` and commit the updated files.
|
|
- **Fixture staleness in `make pre-pr`:** Protocol changed but fixtures were not regenerated. Run `make fixtures && make fixtures-client`, then commit both `client/tests/fixtures/` and `server/tests/fixtures/gdscript/`.
|
|
|
|
### Golden File Management
|
|
|
|
```bash
|
|
make golden-diff # Show diff if golden file output has changed
|
|
make golden-update # Regenerate golden file and stage for commit
|
|
```
|
|
|
|
The golden file (`server/tests/golden/proof_room_tick_10.json`) is a committed snapshot of ObserverSnapshot output after a deterministic 10-tick replay. It catches unintentional changes to simulation output.
|
|
|
|
**Workflow after intentional simulation changes:**
|
|
|
|
1. Run `make golden-diff` to see what changed
|
|
2. Review the diff — confirm changes are expected
|
|
3. Run `make golden-update` to regenerate and stage the new golden file
|
|
4. Commit the updated golden file alongside your simulation change
|
|
|
|
`golden-diff` exits 1 if the golden file has changed (useful in scripts). `golden-update` regenerates the file and runs `git add` but does not commit — the developer reviews and commits manually.
|
|
|
|
### Lint
|
|
|
|
```bash
|
|
make lint # Run all linters
|
|
make lint-server # clippy (deny warnings) + rustfmt --check
|
|
make lint-client # gdlint/gdformat (pending setup)
|
|
```
|
|
|
|
### CI (Local)
|
|
|
|
Run the full CI pipeline locally before pushing:
|
|
|
|
```bash
|
|
make ci # Both pipelines
|
|
make ci-server # lint-server → build-server → test-server
|
|
make ci-client # lint-client → build-client → test-client
|
|
```
|
|
|
|
CI targets chain lint → build → test sequentially. A failure in any stage stops the pipeline.
|
|
|
|
### Pre-PR Checks
|
|
|
|
Before pushing a PR, run:
|
|
|
|
```bash
|
|
make pre-pr
|
|
```
|
|
|
|
This runs all checks in order: lint → build → test → content validation → fixture staleness. Total runtime ~2.5 minutes (incremental build), under 3 minutes clean.
|
|
|
|
For branch-specific checks:
|
|
|
|
```bash
|
|
make pre-pr-server # Server changes: lint, build, test, fixture staleness
|
|
make pre-pr-client # Client changes: lint, build, test
|
|
make pre-pr-content # Content changes: schema + cross-reference validation
|
|
```
|
|
|
|
If `pre-pr-fixtures` fails, your protocol changes require fixture regeneration:
|
|
|
|
```bash
|
|
make fixtures
|
|
git add client/tests/fixtures/
|
|
git commit -m "chore(fixtures): regenerate for protocol v8"
|
|
```
|
|
|
|
The fixture staleness check is a **blocker** (exit 1) — stale fixtures cause false positive client tests.
|
|
|
|
### Clean
|
|
|
|
```bash
|
|
make clean # Remove build artifacts and .cache/ contents
|
|
```
|
|
|
|
### Content Validation
|
|
|
|
```bash
|
|
make validate-content # Validate content YAML against JSON schemas
|
|
make check-fact-ids # Check fact_id references against knowledge catalogs
|
|
```
|
|
|
|
### Gauntlet Checklists
|
|
|
|
```bash
|
|
make checklist-validate # Validate checklist YAML against schema (standalone)
|
|
make checklist-generate # Validate + print per-room condition summary
|
|
```
|
|
|
|
Checklists live at `content/gauntlet/rooms/{room_id}/checklist.yaml` (per-room) and `content/gauntlet/cross_room_checks.yaml` (cross-room). Each condition is evaluable from an `ObserverSnapshot`.
|
|
|
|
7 condition types: `player_near`, `player_facing`, `entity_present`, `entity_absent`, `expected_monologue`, `expected_dialogue`, `expected_interaction_verb`.
|
|
|
|
Schema: `content/_schema/checklist.schema.json`. The checklist format feeds into #503 (client auto-checklist progress tracking).
|
|
|
|
`check-fact-ids` operates in two modes:
|
|
- **Advisory** — when knowledge catalogs (`content/global/knowledge/*.yaml`) have no fact definitions yet: lists referenced fact_ids and exits cleanly.
|
|
- **Enforcing** — when catalogs are populated: fails on any `fact_id` reference that doesn't match a canonical definition.
|
|
|
|
## Pre-commit Hooks
|
|
|
|
Git hooks are stored in `.config/hooks/` (version-controlled). Activate them with:
|
|
|
|
```bash
|
|
make setup # Includes hook installation
|
|
make setup-hooks # Just hooks
|
|
```
|
|
|
|
Or manually:
|
|
|
|
```bash
|
|
git config core.hooksPath .config/hooks
|
|
```
|
|
|
|
Active checks:
|
|
|
|
| Check | Script | Behavior |
|
|
|-------|--------|----------|
|
|
| fact_id validation | `tooling/check-fact-ids` | Warns if catalogs are stubs; fails on unknown fact_ids when populated |
|
|
|
|
The `core.hooksPath` setting uses a relative path (`.config/hooks`) that resolves per worktree, so it works correctly across all worktrees in the repository.
|
|
|
|
To bypass hooks in an emergency:
|
|
|
|
```bash
|
|
git commit --no-verify -m "fix: emergency hotfix"
|
|
```
|
|
|
|
## Configuration Files
|
|
|
|
The `.config/` directory holds shared configuration for linters, formatters, and CI. Examples of what goes here:
|
|
|
|
- Clippy configuration overrides
|
|
- gdlint/gdformat rules
|
|
- CI workflow definitions (before moving to `.github/workflows/`)
|
|
- Editor config templates
|
|
|
|
Project-specific config that lives in subdirectories (e.g., `server/Cargo.toml`, `client/project.godot`) stays in those directories. `.config/` is for cross-cutting or shared configuration.
|
|
|
|
## Cache Directory
|
|
|
|
`.cache/` is gitignored and used for:
|
|
|
|
- Test result caches
|
|
- Linter caches
|
|
- Build artifact caches (if configured)
|
|
- Coverage reports
|
|
|
|
Agents and CI jobs can write freely to `.cache/` without polluting the working tree. `make clean` clears it.
|
|
|
|
## Testing Architecture (D-030)
|
|
|
|
Three-layer testing strategy:
|
|
|
|
1. **Unit tests** — Inside `server/` (Rust `#[cfg(test)]`) and `client/` (gdUnit4). Test individual systems in isolation.
|
|
2. **Integration tests** — Inside `server/tests/` (Rust) and `tests/` (cross-boundary). Test system interactions, IPC serialization round-trips.
|
|
3. **Fixture-based tests** — IPC serialization fixture files in `tests/` for protocol regression testing. Known-good MessagePack payloads verified against both sides.
|
|
|
|
Key components:
|
|
- **CauseChain** (production ECS component) — Tracks causal attribution for testable observation sequences (D-030).
|
|
- **Deterministic replay** — Server simulation is deterministic given the same seed + input sequence. Replay logs enable regression testing (#201, critical).
|
|
|
|
## SQLite Access
|
|
|
|
**Never use the `sqlite3` CLI** — it crashes in Claude Code (std::bad_alloc).
|
|
|
|
Use wrapper scripts:
|
|
```bash
|
|
tooling/db/sqlite-query "SELECT * FROM tickets WHERE status='open'"
|
|
tooling/db/sqlite-exec "UPDATE tickets SET status='done' WHERE id=1"
|
|
```
|
|
|
|
## Qdrant / Document Search
|
|
|
|
```bash
|
|
tooling/db/qdrant-search "asymmetric information design"
|
|
tooling/db/qdrant-index docs/briefings/tyre.md
|
|
tooling/db/qdrant-health
|
|
tooling/db/qdrant-count
|
|
```
|
|
|
|
## Decisions System
|
|
|
|
Decisions are split into domain files under `decisions/` (see `decisions/README.md` for the full index). A SQLite index table syncs metadata for cross-referencing and querying.
|
|
|
|
```bash
|
|
make decisions-sync # Parse decisions/*.md into SQLite
|
|
make decisions-coverage # Decision-to-ticket coverage by domain
|
|
make decisions-active # List all active confirmed decisions
|
|
make decisions-orphan # Decisions without implementing tickets
|
|
```
|
|
|
|
The sync runs automatically as part of `make setup` and via pre-commit hook. Markdown files are the source of truth; the DB is a derived index.
|
|
|
|
## Commit Conventions
|
|
|
|
See the `/git-commit` skill (`.claude/skills/git-commit/`) for full details. Summary:
|
|
|
|
- Conventional commits: `type(scope): summary`
|
|
- Types: `feat`, `fix`, `refactor`, `chore`, `docs`, `data`, `loc`
|
|
- Scopes match project subsystems: `client`, `server`, `engine`, `simulation`, `ui`, `audio`, `meta`, etc.
|
|
- Imperative mood, lowercase, no period, max 72 chars
|
|
- CHANGELOG.md updated after each commit group
|