Files
settled-reach/Makefile
T
jpmschweitzerandClaude Opus 5 cb5d3f1335 refactor(config): T-1281 — the check domain retires its five scripts
All five gates are ported, tested against their failure paths, and the
originals are gone. reach check is the only way to run them.

Parity first, then deletion. Every case in test_check.py began as a parity case
running the new implementation beside the script it replaced; that evidence is
in the ticket. With the scripts retired there is nothing left to compare
against, so the assertions become the spec and the file drops its "_parity"
name. A parity test is scaffolding with a defined lifetime — keeping one after
its subject is deleted would mean keeping the subject alive to be compared
with, which is the opposite of a migration.

Two gates could not be parity-tested in a fixture at all, and both reasons are
findings rather than obstacles. canvas-version: canvas_sources globs from a
__file__ root while the service resolves git through config.repo_root(), so a
fixture would diff one tree and glob another — real history is used instead,
including two genuine instances of the regression the gate exists to catch.
systems-db-stamp: generator_sources raises at IMPORT time when the economy-db
tree is absent, so the old script died before reaching any logic in every
fixture. The ported service imports it lazily and after the absent/unstamped
checks, which is exactly why those states are testable now and were not before.

Hooks rewired: pre-commit runs reach check fact-ids, pre-push runs the other
four. Both pass --no-input, because a hook has no TTY and a prompt there does
not wait, it crashes. Both guard on `command -v reach` and skip with a message
rather than blocking every commit on a missing tool.

Make targets are RETIRED, not wrapped, per the D-263 split — with the mapping
left as a comment where they used to be. Wrapping would leave two ways to
invoke each gate, and reach --help would stop being the answer to "what tooling
exists" while the Makefile remained a competing index. pre-pr-validate and
pre-pr-content keep their orchestration role and lose the individual target.

Sprint archives and workshop notes still name the old paths and are left alone:
they record what was true when written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:33 +02:00

624 lines
27 KiB
Makefile

GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
.PHONY: help setup build client server game atlas stop test test-tooling lint lint-python setup-venv ci ci-client ci-server clean \
install-reach reach-repoint \
decisions-sync decisions-active decisions-validate \
validate-content setup-hooks install-hooks \
audit deny atlas-verify economy-db regen-db \
pre-pr pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures \
pre-pr-server pre-pr-client pre-pr-content \
fixtures-client fixtures-gauntlet golden-diff golden-update \
checklist-validate checklist-generate check-star-map star-map-data \
build-sr-voice run-sr-voice test-voice-mock test-voice-real \
perf-baseline debug-schedule \
test-ipc-fixtures test-ipc-protocol test-ipc-integration test-ipc-benchmark \
screenshot visual-movie test-visual visual-update \
manifest diagrams check-diagrams
# --- Configuration ---
GODOT_VERSION ?= 4.6
# One interpreter for both .venv and the installed `reach` tool. Pinned rather
# than inferred: uv otherwise picks the lowest version satisfying
# requires-python, which forks the two environments silently (T-1261).
PYTHON_VERSION ?= 3.14
# Default
help:
@echo "The Settled Reach — Development Commands"
@echo ""
@echo " make setup Install dev dependencies (Rust, Godot, tooling, venv)"
@echo " make setup-venv Create .venv and install Python tooling deps"
@echo " make install-reach Install the 'reach' CLI onto PATH (bare name, no venv needed)"
@echo " make reach-repoint Re-point 'reach' at THIS checkout (after worktree work)"
@echo " make build Build client and server"
@echo " make game Build and run the full game (server + client)"
@echo " make atlas Standalone Atlas companion app — attach or spawn, read-only (D-254)"
@echo " make stop Stop any running server instance"
@echo " make client Run the Godot client (test mode)"
@echo " make server Run the Rust simulation server"
@echo " make test Run all tests"
@echo " make test-tooling Tooling test gate: sim determinism + economics dry-run"
@echo " make test-ipc-fixtures Layer 1: IPC serialization fixtures"
@echo " make test-ipc-protocol Layer 2: mock IPC protocol tests"
@echo " make test-ipc-integration Layer 3: real subprocess round-trip"
@echo " make test-ipc-benchmark IPC latency benchmark (blocked: #555/#556)"
@echo " make lint Run all linters (server, client, python)"
@echo " make lint-python Run ruff on tooling/"
@echo " make ci Run full CI pipeline locally"
@echo " make ci-client Run client CI checks"
@echo " make ci-server Run server CI checks"
@echo " make clean Remove build artifacts and caches"
@echo ""
@echo " make decisions-sync Sync governance/*.md decision records into pql.db"
@echo " make decisions-active List active confirmed decisions (pql)"
@echo " make decisions-validate Validate decision records — malformed-record gate (pql)"
@echo " make audit Run cargo audit (security advisory check)"
@echo " make deny Run cargo deny check (license/ban policy)"
@echo " make validate-content Validate content YAML against schemas"
@echo " make atlas-verify Verify atlas proposal JSONs (all in docs/atlas/proposals/)"
@echo " make star-map-data Regenerate client/data/star_map_data.json from systems.db + wiki"
@echo " make check-star-map Assert star_map_data.json is up to date (part of pre-pr-client)"
@echo " make economy-db Import economics data into systems.db (TOML/JSON → SQLite)"
@echo " make regen-db Regenerate systems.db from all sources + stamp meta table (#855)"
@echo " make install-hooks Install pre-push + pre-commit git hooks (once per clone)"
@echo " make fixtures-client Generate GDScript->Rust cross-encoder fixtures (#475)"
@echo " make golden-diff Show diff if golden file output has changed"
@echo " make golden-update Regenerate golden file and stage for commit"
@echo " make checklist-validate Validate checklist YAML against schema"
@echo " make checklist-generate Validate checklists + print condition summary"
@echo " make perf-baseline Run performance benchmarks and save baseline"
@echo ""
@echo " make screenshot Ad-hoc visual capture (SCENARIO=name, default: atlas_GJ820Bc_Global)"
@echo " make visual-movie Flow capture with contact sheet (FLOW=name)"
@echo " make test-visual Run visual golden regression tests"
@echo " make visual-update Regenerate visual goldens and stage for commit"
@echo ""
@echo " make manifest Regenerate assets/characters/manifest.json from asset dirs (#720)"
@echo " make pre-pr Run all pre-PR checks (lint, build, test, validate, fixtures)"
@echo " make pre-pr-server Server-scoped pre-PR (lint, build, test, fixtures)"
@echo " make pre-pr-client Client-scoped pre-PR (lint, build, test)"
@echo " make pre-pr-content Content-scoped pre-PR (schema + cross-ref validation)"
@echo ""
@echo " make setup-hooks Install pre-commit hooks (included in setup)"
@echo " make build-sr-voice Build sr-voice LLM inference service"
@echo " make serve-sr-voice Start sr-voice server (ARGS='--model <path>')"
@echo " make run-sr-voice Submit to sr-voice server (ARGS='generate|batch|benchmark ...')"
@echo " make stop-sr-voice Stop sr-voice server"
@echo " make test-voice-mock Test voice pipeline with mock sr-voice"
@echo " make test-voice-real Test voice pipeline with real sr-voice + Gemma 2B"
@echo " make debug-schedule Print bevy_ecs schedule graph (diff for PR artifacts)"
@echo ""
@echo " GODOT_VERSION=4.6 make setup Override Godot version"
# --- Setup ---
setup: setup-rust setup-godot setup-tooling setup-venv install-reach setup-hooks decisions-sync
@echo "Dev environment ready."
setup-rust:
@tooling/install-rust
setup-godot:
@GODOT_VERSION=$(GODOT_VERSION) tooling/install-godot
setup-tooling:
@echo "Checking tooling dependencies..."
@command -v python3 >/dev/null 2>&1 || { echo "Install Python 3"; exit 1; }
@command -v curl >/dev/null 2>&1 || { echo "Install curl (required for Godot download)"; exit 1; }
@command -v unzip >/dev/null 2>&1 || { echo "Install unzip (required for Godot download)"; exit 1; }
@python3 -c "import yaml" 2>/dev/null || { echo "Install PyYAML: pip install pyyaml"; exit 1; }
@python3 -c "import jsonschema" 2>/dev/null || { echo "Install jsonschema: pip install jsonschema"; exit 1; }
setup-hooks:
@git config core.hooksPath .config/hooks
@echo "Git hooks path set to .config/hooks"
install-hooks: setup-hooks
@chmod +x .config/hooks/pre-push .config/hooks/pre-commit \
.config/hooks/post-merge .config/hooks/post-checkout .config/hooks/post-rewrite
@echo "Hooks installed — pre-commit, pre-push, post-merge, post-checkout, post-rewrite are active."
setup-venv:
@command -v uv >/dev/null 2>&1 || { echo "Install uv: https://docs.astral.sh/uv/"; exit 1; }
@uv venv --python $(PYTHON_VERSION) --allow-existing .venv
@uv pip install --python .venv/bin/python -e ".[dev]" --quiet
@echo "Venv ready at .venv — activate with: source .venv/bin/activate"
@echo "NOTE: activating is for running tests. The reach CLI does not need it — see 'make install-reach'."
# The PATH guarantee (T-1261, D-263). A [project.scripts] entrypoint alone lands
# in .venv/bin, which is on PATH only while the venv is activated — and agents
# and git hooks never activate it. `uv tool install` puts the executable in
# ~/.local/bin instead, so `reach` is a bare name everywhere: interactive shell,
# git hook, agent Bash call.
#
# --editable so the checkout IS the source: edit tooling/, run reach, no reinstall.
# --python pinned so the tool and .venv run the SAME interpreter; left to itself
# uv picks the lowest version satisfying requires-python (it chose 3.11 while the
# venv was on 3.14), which is a silent interpreter fork of exactly the kind this
# initiative exists to remove.
install-reach:
@command -v uv >/dev/null 2>&1 || { echo "Install uv: https://docs.astral.sh/uv/"; exit 1; }
@uv tool install --python $(PYTHON_VERSION) --editable . --force
@command -v reach >/dev/null 2>&1 || { \
echo "FAIL: 'reach' is not on PATH. Add ~/.local/bin to PATH (uv tool update-shell)."; exit 1; }
@echo "reach installed — verify with: reach --help"
# Re-point reach at THIS checkout. uv records the source path at install time, so
# an install made from a worktree keeps resolving there after the worktree is
# removed, and edits in the main checkout then have no effect on a working `reach`
# — a confusing failure with no error message. Same command; the alias exists so
# the situation has a name.
reach-repoint: install-reach
@echo "reach now resolves to: $(CURDIR)"
# --- Build ---
build: build-server build-client
build-server:
cd server && cargo build
build-server-release:
cd server && cargo build --release
build-client:
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
@# First import may error on theme/font loading before the import scan completes.
@# Run twice: first pass generates imports silently, second pass validates clean.
@$(GODOT) --headless --path client --import --quit 2>/dev/null || true
$(GODOT) --headless --path client --import --quit
# --- Run ---
server:
cd server && cargo run --bin settled-reach-server
client:
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
$(GODOT) --path client
game: stop build
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
@echo "Starting server..."
@cd server && cargo run --bin settled-reach-server &
@sleep 2
@echo "Starting client..."
@SR_LIVE=1 $(GODOT) --path client
@$(MAKE) stop
# D-254/T-1132: standalone Atlas companion app. Unlike `game`, no orchestration
# here — atlas_standalone.gd owns its own attach-or-spawn decision and child
# server lifecycle internally (D-254 §1/§3), so this target is just "build,
# then launch the standalone scene". build-server ensures the debug binary
# exists for spawn-mode (atlas_standalone.gd resolves it at
# server/target/debug/settled-reach-server, the same path the E2E test files
# use); build-client ensures imports are current. SR_LIVE=1 is required —
# without it SimBridge boots in test mode (instant fake CONNECTED, no network
# at all), which would make the companion open the Atlas against a dynamic
# test-harness snapshot instead of the real systems.db world via the wire.
# Release server for the atlas: a cold DEBUG server takes >10s before the
# first tile of a new body can exist (first-body terrain analysis), which
# reads as a black/broken map on entry (live-verified 2026-07-22). The
# release binary serves a cold body's first tiles in well under a second.
# SR_SERVER_BIN tells atlas_standalone.gd's spawn path which binary to use.
atlas: build-server-release build-client
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
@SR_LIVE=1 SR_SERVER_BIN=server/target/release/settled-reach-server $(GODOT) --path client scenes/atlas_standalone.tscn
stop:
@lsof -ti :9876 | xargs -r kill 2>/dev/null || true
@echo "Stopped any running server on port 9876"
# --- Test ---
test: test-server test-client
test-server:
tests/run-rust
fixtures:
cd server && cargo test --test gen_fixtures -- --ignored
fixtures-gauntlet:
cd server && cargo test --test gen_gauntlet_fixtures -- --ignored
fixtures-client:
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
@echo "Generating GDScript fixtures for Rust decoder..."
$(GODOT) --headless --path client -s res://tests/gen_client_fixtures.gd
@echo "Verifying Rust can decode GDScript fixtures..."
cd server && cargo test --test serialization gdscript_generated_fixtures
@echo "--- Fixtures-client: PASS ---"
golden-diff:
@echo "Regenerating golden output for comparison..."
@mkdir -p .cache/golden
@cp server/tests/golden/proof_room_tick_10.json .cache/golden/before.json
@cd server && UPDATE_GOLDEN=1 cargo test --test golden_suite -- proof_room_tick_10_matches_golden; \
rc=$$?; \
cp ../server/tests/golden/proof_room_tick_10.json ../.cache/golden/after.json 2>/dev/null; \
cp ../.cache/golden/before.json ../server/tests/golden/proof_room_tick_10.json; \
if [ $$rc -ne 0 ]; then echo "Cargo test failed (golden file restored)."; exit $$rc; fi
@if diff -q .cache/golden/before.json .cache/golden/after.json >/dev/null 2>&1; then \
echo "--- Golden file: UP TO DATE ---"; \
else \
echo "--- Golden file has CHANGED ---"; \
echo ""; \
diff --color -u .cache/golden/before.json .cache/golden/after.json || true; \
echo ""; \
echo "Run 'make golden-update' to accept changes."; \
exit 1; \
fi
golden-update:
@echo "Regenerating golden files..."
@cd server && UPDATE_GOLDEN=1 cargo test --test golden_suite -- proof_room_tick_10_matches_golden
@cd server && UPDATE_GOLDEN=1 cargo test --test cascade_golden
@git add server/tests/golden/
@echo "--- Golden file updated and staged ---"
@echo "Review with: git diff --cached -- server/tests/golden/"
test-client:
tests/run-godot
test-ipc-fixtures:
tests/run-ipc-fixtures
test-ipc-protocol:
tests/run-ipc-protocol
test-ipc-integration:
tests/run-ipc-integration
test-ipc-benchmark:
tests/run-ipc-benchmark
# Python with tooling deps (numpy/scipy) — .venv from `make setup-venv`, else system python3
VENV_PY := $(shell test -x .venv/bin/python && echo .venv/bin/python || echo python3)
# Tooling test gate (T-1066) — called by pre-push when tooling/ changed.
# 1. planet-gen determinism guard (#963): simulating the same body twice must
# be bit-identical (guards the expensive 271-body heightmap bake).
# Exit 2 = no testable body found — warn, don't block.
# 2. import_economics --dry-run against the committed DB: full parse +
# structural/coverage validation, no writes. Exit 2 = coverage-gate
# warning (D-175) — tolerated, matching regen-db's treatment.
test-tooling:
@echo " [test-tooling] planet-gen determinism guard (#963)..."
@rc=0; $(VENV_PY) tooling/planet-gen/test_sim_determinism.py || rc=$$?; \
if [ $$rc -eq 2 ]; then \
echo " WARNING: determinism guard found no testable body (exit 2) — not blocking"; \
elif [ $$rc -ne 0 ]; then \
echo " FAIL: planet_simulation determinism drift (exit $$rc)"; exit $$rc; \
fi
@echo " [test-tooling] oasis ring-scaling pin (T-964, PR #210 review)..."
@$(VENV_PY) tooling/planet-gen/test_oasis_ring_scaling.py 2> .cache/test-tooling-oasis-ring.log || \
{ echo " FAIL: oasis ring scaling — log follows:"; cat .cache/test-tooling-oasis-ring.log; exit 1; }
@echo " [test-tooling] canvas-generation version gate units (T-1242)..."
@mkdir -p .cache
@python3 tooling/test_canvas_version_check.py 2> .cache/test-tooling-canvas-version.log || \
{ echo " FAIL: canvas version gate units — log follows:"; cat .cache/test-tooling-canvas-version.log; exit 1; }
@echo " [test-tooling] reach lazy domain registration (T-1260)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_lazy_domains.py 2> .cache/test-tooling-lazy-domains.log || \
{ echo " FAIL: reach lazy registration — log follows:"; cat .cache/test-tooling-lazy-domains.log; exit 1; }
@echo " [test-tooling] reach detached-job exit codes (T-1279)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_job_exit_codes.py 2> .cache/test-tooling-job-exits.log || \
{ echo " FAIL: detached exit codes — log follows:"; cat .cache/test-tooling-job-exits.log; exit 1; }
@echo " [test-tooling] reach jobs service units (T-1278)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_jobs.py 2> .cache/test-tooling-jobs.log || \
{ echo " FAIL: jobs service — log follows:"; cat .cache/test-tooling-jobs.log; exit 1; }
@echo " [test-tooling] reach D-263 conformance (T-1270)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_conformance.py 2> .cache/test-tooling-conformance.log || \
{ echo " FAIL: reach conformance — log follows:"; cat .cache/test-tooling-conformance.log; exit 1; }
@echo " [test-tooling] reach check gates — behaviour (T-1281)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_check.py 2> .cache/test-tooling-check.log || \
{ echo " FAIL: check gates — log follows:"; cat .cache/test-tooling-check.log; exit 1; }
@echo " [test-tooling] economy_import.traits validation units (T-995/PR #173 H2)..."
@mkdir -p .cache
@python3 tooling/economy-db/test_traits.py 2> .cache/test-tooling-traits.log || \
{ echo " FAIL: traits validation units — log follows:"; cat .cache/test-tooling-traits.log; exit 1; }
@echo " [test-tooling] atlas_city_names/atlas_feature_names idempotency (T-964)..."
@python3 tooling/economy-db/test_atlas_idempotency.py 2> .cache/test-tooling-atlas-idempotency.log || \
{ echo " FAIL: atlas name-pool idempotency — log follows:"; cat .cache/test-tooling-atlas-idempotency.log; exit 1; }
@echo " [test-tooling] import_economics --dry-run (committed DB)..."
@rc=0; python3 tooling/economy-db/import_economics.py --dry-run \
> .cache/test-tooling-dryrun.log 2>&1 || rc=$$?; \
if [ $$rc -eq 2 ]; then \
echo " WARNING: coverage gate warning (exit 2) — not blocking (matches regen-db)"; \
elif [ $$rc -ne 0 ]; then \
echo " FAIL: import_economics --dry-run exited $$rc — log follows:"; \
cat .cache/test-tooling-dryrun.log; exit $$rc; \
fi
@echo " test-tooling: PASS"
# --- Clean ---
clean-imports:
@echo "Removing Godot import cache..."
rm -rf client/.godot/imported/
@echo "Re-importing assets (this may take a while on first run)..."
$(GODOT) --headless --path client --import --quit
@echo "Import cache rebuilt."
# --- Lint ---
lint: lint-server lint-client lint-python
lint-python:
ruff check tooling/
lint-server:
cd server && cargo clippy -- -D warnings
cd server && cargo fmt --check
lint-client:
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
@echo "Checking GDScript for errors..."
@$(GODOT) --headless --path client --quit 2>&1 | grep -i "SCRIPT ERROR" && { echo "GDScript errors found"; exit 1; } || echo "No script errors found"
# --- Pre-PR verification ---
pre-pr: pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures audit deny
@echo ""
@echo "=== PRE-PR: ALL CHECKS PASSED ==="
@echo "Safe to create PR."
pre-pr-lint: lint-server lint-client
@echo "--- Lint: PASS ---"
pre-pr-build: build-server build-client
@echo "--- Build: PASS ---"
pre-pr-test: test-server test-client
@echo "--- Tests: PASS ---"
pre-pr-validate: validate-content check-star-map
@echo "--- Content validation: PASS ---"
pre-pr-fixtures:
@echo "Checking Rust->GDScript fixture staleness..."
@cd server && cargo test --test gen_fixtures -- --ignored
@if git diff --quiet client/tests/fixtures/; then \
echo "--- Rust fixtures: UP TO DATE ---"; \
else \
echo ""; \
echo "--- RUST FIXTURES STALE ---"; \
echo " Protocol changed but fixtures not regenerated."; \
echo " Stale fixtures make all client tests FALSE POSITIVES."; \
echo ""; \
echo " Changed files:"; \
git diff --stat client/tests/fixtures/; \
echo ""; \
echo " Fix: commit the updated fixtures with your protocol change."; \
exit 1; \
fi
@echo "Checking GDScript->Rust fixture staleness..."
@$(MAKE) fixtures-client
@if git diff --quiet server/tests/fixtures/gdscript/; then \
echo "--- GDScript fixtures: UP TO DATE ---"; \
else \
echo ""; \
echo "--- GDSCRIPT FIXTURES STALE ---"; \
echo " Protocol changed but GDScript fixtures not regenerated."; \
echo " Stale fixtures make cross-encoder tests FALSE POSITIVES."; \
echo ""; \
echo " Changed files:"; \
git diff --stat server/tests/fixtures/gdscript/; \
echo ""; \
echo " Fix: commit the updated fixtures with your protocol change."; \
exit 1; \
fi
# Branch-specific variants (faster, scope-appropriate)
pre-pr-server: lint-server build-server test-server pre-pr-fixtures audit deny
@echo "=== Server pre-PR: PASSED ==="
pre-pr-client: lint-client build-client test-client check-star-map
@echo "=== Client pre-PR: PASSED ==="
pre-pr-content: validate-content checklist-validate atlas-verify
@echo "=== Content pre-PR: PASSED ==="
# --- CI (run locally) ---
ci: ci-server ci-client
ci-server: lint-server build-server test-server
ci-client: lint-client build-client test-client
# --- Database ---
economy-db: ## Import economics data (commodities, chains, gate links) into systems.db
@echo " Generating minor brands (D-189 #829)..."
@tooling/generate-brands
@python3 tooling/economy-db/import_economics.py
regen-db: ## Regenerate systems.db from all sources and stamp meta table (#855, #856)
@# Run as a single shell so `set -e` covers all steps. Without this
@# each recipe line was a fresh shell and a failure in step 1 did not
@# halt step 2, which could produce stale data with a fresh stamp
@# (PR #136 review T4). import_economics' exit code 2 is a valid
@# coverage-gate-warning state (DB and stamp committed), not an error,
@# so it's explicitly tolerated. Any other non-zero exit halts the
@# pipeline immediately. The atlas city/road/river geometry generator
@# was retired in #951 (D-223); import_economics now owns the atlas
@# index — it loads the names-only pool into atlas_city_names and empties
@# the geometry tables (the server cascade fills them, Phase 4).
@set -e; \
echo " [regen-db] Importing economics data (runs generate_brands internally)..."; \
ec=0; python3 tooling/economy-db/import_economics.py || ec=$$?; \
if [ $$ec -ne 0 ] && [ $$ec -ne 2 ]; then exit $$ec; fi; \
echo ""; \
echo " regen-db complete — systems.db is up to date and stamped."; \
echo " Stage it with: git add server/data/systems.db"
# The five gate targets that lived here are RETIRED, not wrapped (D-263,
# T-1281). They are tooling, and tooling has one door:
#
# make check-systems-db -> reach check systems-db-stamp
# make check-client-version -> reach check client-version
# make check-canvas-version -> reach check canvas-version
# make check-fact-ids -> reach check fact-ids
# make check-dataflow-graph -> reach check dataflow-graph
#
# Wrapping them would leave two ways to invoke each, and `reach --help` would
# stop being the answer to "what tooling exists" because the Makefile would
# still be a competing index. make keeps build and test ORCHESTRATION; it does
# not keep aliases for individual tools.
econ-sim: ## Build the economics simulation binary (Layer 1+2: Leontief + tâtonnement trade)
@cargo build --manifest-path tooling/econ-sim/Cargo.toml --release
@echo "Built: tooling/econ-sim/target/release/econ-sim"
econ-sim-run: ## Run a quick economics simulation (100 ticks, output to /tmp/econ-sim.csv)
@tooling/econ-sim/target/release/econ-sim --ticks 100 --output /tmp/econ-sim.csv
@echo "Output: /tmp/econ-sim.csv"
econ-sim-stability: ## Run D-179 stability checks (Tests 1 and 2)
@tooling/econ-sim/target/release/econ-sim --stability-check
# --- Decisions (pql) ---
# Decision records are markdown-sourced under governance/{decisions,questions,rejected}/
# and indexed into .pql/pql.db by `pql decisions sync`. Per-decision ticket coverage:
# `pql decisions show <id> --with-tickets`. Cross-references: `pql decisions refs <id>`.
decisions-sync:
@pql decisions sync
decisions-active:
@pql decisions list --type confirmed
decisions-validate:
@pql decisions validate
# --- Content Validation ---
validate-content:
@tooling/validate-content
atlas-verify:
@tooling/atlas-verify docs/atlas/proposals/*.json
audit:
cd server && cargo audit
deny:
cd server && cargo deny check
checklist-validate:
@tooling/validate-checklist --check
checklist-generate:
@tooling/validate-checklist
check-star-map:
@python3 tooling/generate-star-map-data.py --check
# Regenerate client/data/star_map_data.json from systems.db + wiki. Depends on
# nothing — call this after any systems.db change (e.g. the server atlas
# pipeline populating terrain_reference in #839) so the atlas viewer picks up
# the new fields. pre-pr-client / pre-pr-validate assert staleness via
# check-star-map and will fail if this step is skipped.
star-map-data:
@python3 tooling/generate-star-map-data.py
perf-baseline:
@tooling/perf-baseline
# --- Schedule debug (#346) ---
debug-schedule:
@echo "Dumping bevy_ecs schedule graph..."
@cd server && cargo run --bin settled-reach-server -- --dump-schedule
# --- Visual test harness ---
screenshot:
@tests/run-visual --screenshot $(SCENARIO)
visual-movie:
@tests/run-visual --movie $(FLOW)
test-visual:
@tests/run-visual
visual-update:
@tests/run-visual --update
LIBCLANG_PATH ?= /usr/lib64/rocm/llvm/lib
BINDGEN_CLANG_ARGS ?= -I/usr/lib64/rocm/llvm/lib/clang/19/include
SR_VOICE_ENV = LIBCLANG_PATH=$(LIBCLANG_PATH) BINDGEN_EXTRA_CLANG_ARGS="$(BINDGEN_CLANG_ARGS)"
SR_VOICE_PORT ?= 8321
build-sr-voice:
cd server/sr-voice && $(SR_VOICE_ENV) cargo build --release
serve-sr-voice:
cd server/sr-voice && $(SR_VOICE_ENV) cargo run --release -- serve $(ARGS)
run-sr-voice:
cd server/sr-voice && $(SR_VOICE_ENV) cargo run --release -- $(ARGS)
stop-sr-voice:
@lsof -ti :$(SR_VOICE_PORT) | xargs -r kill 2>/dev/null || true
@echo "Stopped sr-voice on port $(SR_VOICE_PORT)"
test-voice-mock:
@echo "Running voice pipeline test (mock sr-voice)..."
cd server && SR_VOICE_MOCK=1 cargo test --test voice_pipeline -- --nocapture
@echo "Results: .tmp/voice-test/results.txt"
test-voice-real:
@echo "Running voice pipeline test (real sr-voice + Gemma 2B)..."
@test -f server/sr-voice/target/release/sr-voice || { echo "Build sr-voice first: make build-sr-voice"; exit 1; }
@test -f server/models/gemma2.gguf || { echo "Model not found: server/models/gemma2.gguf"; exit 1; }
cd server && cargo test --test voice_pipeline -- --nocapture
@echo "Results: .tmp/voice-test/results.txt"
# --- Asset manifest ---
manifest:
@tooling/generate-character-manifest
@echo "Manifest regenerated — commit client/assets/characters/manifest.json if changed."
# --- Diagrams ---
#
# SVG, not PNG: d2 emits SVG natively, while its PNG path wants a ~150 MB
# headless-Chromium download and prompts interactively. SVG also renders in
# Gitea and in clide (`clide draw --file <path.d2|.svg>`), diffs as text, and
# cut docs/diagrams/ from 17 MB to 2.8 MB.
#
# Do NOT use d2 `|md` blocks in a diagram: they emit an SVG <foreignObject>,
# which ImageMagick and flutter_svg both silently drop — the text is then in
# the file and invisible in every viewer except a browser. Use plain labels.
diagrams:
@for d in $$(find docs/diagrams -name '*.d2'); do \
d2 "$$d" "$${d%.d2}.svg" >/dev/null || exit 1; \
done
@echo "Diagrams rendered — commit any changed docs/diagrams/**/*.svg."
check-diagrams:
@fail=0; for d in $$(find docs/diagrams -name '*.d2'); do \
test -f "$${d%.d2}.svg" || { echo "unrendered: $$d"; fail=1; }; \
done; \
test $$fail -eq 0 || { echo "Run 'make diagrams'."; exit 1; }; \
echo "All diagrams rendered."
# --- Clean ---
clean:
cd server && cargo clean || true
rm -rf .cache/*
rm -rf client/reports
@echo "Clean complete."