Files
settled-reach/Makefile
T
jpmschweitzerandClaude Opus 5 7f20bd303b feat(config): T-1282 — the validate domain, and a move that broke a root
reach validate content / checklist / ron / name-collisions. The three old
scripts are retired, their make targets with them.

Print statements go through the logging sink rather than a collector. The
validators emit their findings as console events as they run, so a long content
validation streams instead of going quiet and dumping at the end — the message
strings and their order are unchanged, only the destination. That also
satisfies the conformance rule forbidding print() in the package, which is what
forced the question.

validate-ron was three languages deep: bash dispatching on a flag, a Python
heredoc doing collision detection, cargo run for schema validation. Logic
embedded in a shell string cannot be imported, tested, or found by anything
that indexes Python, so it became Python; the cargo call became a guarded exec.
It also split into two verbs, because --check-name-collisions answered a
different question from the default path: whether the SET of cultures is
coherent, versus whether ONE file is well-formed.

The move broke something, quietly, which is the point of doing these one at a
time. validate-checklist computed ROOT as Path(__file__).parent.parent — the
repo root while it lived at tooling/validate-checklist, and tooling/domains
once moved. Both its schema and gauntlet paths silently repointed at nothing,
the gauntlet directory "did not exist", and it reported success having checked
zero files. Caught by running it beside the original: old exit 1, new exit 0.
Now config.repo_root(), and load_schema raises ReachError instead of calling
sys.exit, which a service must not do.

Parity on the live tree: content reproduces the original byte for byte
including its counts, name-collisions likewise. Tests pin what those runs
cannot reach — the detection path, since the repo currently has no collisions,
and the argument errors.

Two things found and left alone: validate-content FAILS on the live tree with
13 missing schemas, pre-existing and unrelated to this port; and the ticket's
claim that validate-content sits in the pre-commit hook is wrong — that hook
runs only check-fact-ids and pql decisions validate, so there was no shared
edit to coordinate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:48:56 +02:00

619 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 \
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 \
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 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 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 validate verbs — behaviour (T-1282)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_validate.py 2> .cache/test-tooling-validate.log || \
{ echo " FAIL: validate verbs — log follows:"; cat .cache/test-tooling-validate.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: 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: 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
# make validate-content -> reach validate content
# make checklist-validate -> reach validate checklist --check
# make checklist-generate -> reach validate checklist
#
# 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 ---
atlas-verify:
@tooling/atlas-verify docs/atlas/proposals/*.json
audit:
cd server && cargo audit
deny:
cd server && cargo deny check
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."