Files
settled-reach/docs/DEVOPS.md
T
jpmschweitzerandClaude Opus 4.6 5683b1dc4b chore(config): fix skill contradictions, gitignore .obsidian, docs cleanup
- sprint-start: remove three-tier severity (critical/warning/suggestion),
  align with pr-review policy (every comment is actionable)
- sprint-start: resolve {team_scope_dirs} dangling placeholder
- .gitignore: add .obsidian/ directory
- DEVOPS.md: remove "pending setup" from gdlint (now enforced)
- Remove GEMINI-SCAN.md one-off scan artifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:16:51 +02:00

12 KiB

title, description, type, status
title description type status
DevOps Procedures Build, test, lint, and CI procedures for the Settled Reach project — Makefile targets, worktree setup, server/client builds architecture active

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 schema + seed data (connectors at tooling/db/)

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

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

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

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

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:

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:

{"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

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

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

make lint               # Run all linters
make lint-server        # clippy (deny warnings) + rustfmt --check
make lint-client        # gdlint/gdformat

CI (Local)

Run the full CI pipeline locally before pushing:

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:

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:

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:

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

make clean              # Remove build artifacts and .cache/ contents

Content Validation

make validate-content     # Validate content YAML against JSON schemas
make check-fact-ids       # Check fact_id references against knowledge catalogs

Gauntlet Checklists

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:

make setup          # Includes hook installation
make setup-hooks    # Just hooks

Or manually:

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:

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:

tooling/db/sqlite-query "SELECT * FROM tickets WHERE status='open'"
tooling/db/sqlite-exec "UPDATE tickets SET status='done' WHERE id=1"
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.

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