Files
settled-reach/Makefile
T
jpmschweitzerandClaude Opus 5.5 caea76387b test(tooling): T-1257 — reach called by name, and one Python
Two failures T-1261 found and fixed by hand, left unguarded. They now fail
the conformance gate:

- (8) An interpreter-prefixed invocation — `python -m tooling`,
  `python3 -m tooling`, `.venv/bin/reach` — anywhere in the Makefile, the
  hooks, .claude or docs. One such line silently forks the PATH guarantee.
  Lines that FORBID the pattern are exempt, since docs/DEVOPS.md states the
  rule in those words and a check that flags its own documentation gets muted.
- (9) The installed reach (read from its shebang) and the .venv the gate
  tests run in must be the same Python minor version, and it must be the one
  the Makefile pins. uv once installed reach on 3.11 against a 3.14 venv, so
  the tests were proving nothing about the interpreter the hooks run.

Both proven to fail: an appended `python3 -m tooling.main` make target is
caught at its line, and a fake `reach` with a 3.12 shebang first on PATH
trips both version comparisons. The invariant list in the docstring now
covers all nine, not the first five.

Also: the canvas-version units ran under system python3 while every other
gate test uses $(VENV_PY). Now they match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 20:21:32 +02:00

622 lines
28 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 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 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 regen-db Regenerate systems.db + stamp meta table (= reach ledger import)"
@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 ---
# install-reach leads: it needs only uv, and setup-rust/setup-godot are now
# reach verbs. Bootstrapping the CLI before the things it installs is the only
# order that works on a cold checkout.
setup: install-reach setup-rust setup-godot setup-tooling setup-venv setup-hooks decisions-sync
@echo "Dev environment ready."
setup-rust:
@reach dev install-rust
setup-godot:
@reach dev install-godot --version $(GODOT_VERSION)
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. atlas planet 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. reach ledger import --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] atlas planet determinism guard (#963)..."
@rc=0; $(VENV_PY) tooling/test_planet_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/test_planet_oasis_rings.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] reach atlas planet options vs module parsers (T-1288)..."
@$(VENV_PY) tooling/test_planet_router.py 2> .cache/test-tooling-planet-router.log || \
{ echo " FAIL: planet router drift — log follows:"; cat .cache/test-tooling-planet-router.log; exit 1; }
@echo " [test-tooling] reach assets connectors against a fake Gradio (T-1290)..."
@$(VENV_PY) tooling/test_assets.py 2> .cache/test-tooling-assets.log || \
{ echo " FAIL: assets connectors — log follows:"; cat .cache/test-tooling-assets.log; exit 1; }
@echo " [test-tooling] reach character — QA plan and GLB strip (T-1290)..."
@$(VENV_PY) tooling/test_character.py 2> .cache/test-tooling-character.log || \
{ echo " FAIL: character decisions — log follows:"; cat .cache/test-tooling-character.log; exit 1; }
@echo " [test-tooling] canvas-generation version gate units (T-1242)..."
@mkdir -p .cache
@$(VENV_PY) 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 blender carve-out decisions (T-1273)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_blender.py 2> .cache/test-tooling-blender.log || \
{ echo " FAIL: blender decisions — log follows:"; cat .cache/test-tooling-blender.log; exit 1; }
@echo " [test-tooling] reach dev environment decisions (T-1286)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_environment.py 2> .cache/test-tooling-environment.log || \
{ echo " FAIL: environment decisions — log follows:"; cat .cache/test-tooling-environment.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] ledger traits validation units (T-995/PR #173 H2)..."
@mkdir -p .cache
@$(VENV_PY) tooling/test_ledger_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)..."
@$(VENV_PY) tooling/test_ledger_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] reach ledger import --dry-run (committed DB)..."
@rc=0; reach ledger import --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: reach ledger import --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:
@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 ---
# Survives for muscle memory (D-263: "delegates to reach in one line and says
# so") — `make regen-db` is named in ~50 files, including the headers of the
# generated wiki TOMLs and the remedies the push gate prints. The behaviour is
# `reach ledger import`; this only tolerates its exit 2, which means imported
# AND stamped with the D-175 coverage gate unmet — a content gap, not a failure.
regen-db: ## Regenerate systems.db and stamp it — delegates to `reach ledger import`
@ec=0; reach ledger import || ec=$$?; [ $$ec -eq 0 ] || [ $$ec -eq 2 ] || exit $$ec
# The gate targets that lived here are RETIRED, not wrapped (D-263,
# T-1281). They are tooling, and tooling has one door:
#
# make economy-db -> reach ledger import (T-1289; it also ran
# `reach generate brands` first, which the
# import already does as its own first step)
# 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
# make atlas-verify -> reach atlas verify
#
# 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 ---
audit:
cd server && cargo audit
deny:
cd server && cargo deny check
# Both delegate to `reach atlas map data` in one line and say so (D-263):
# check-star-map is a dependency of pre-pr-client / pre-pr-validate, which is
# orchestration, and star-map-data is named in the help. Regenerate after any
# systems.db change (e.g. terrain_reference, #839) so the atlas viewer picks up
# the new fields; the pre-pr targets fail on a stale file.
check-star-map: ## = reach atlas map data --check
@reach atlas map data --check
star-map-data: ## = reach atlas map data
@reach atlas map data
perf-baseline:
@reach dev perf
# --- 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:
@reach 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."