schemas.py becomes pydantic, so the reference domain is the normal pattern rather than an exception carrying a footnote. Frozen: a result is a statement about what was found, and nothing downstream should edit the finding on its way to being reported. pydantic stays off the --help path — test_lazy_domains still passes, which is precisely the assertion that it loads with the domain and not with the CLI. The acceptance criterion could not be met as written, and that is the finding worth keeping. It asked for byte-for-byte parity with the old script; D-263 was amended after this ticket to give reach a streaming model that puts the verdict on stderr, while the old script writes its success line to stdout. Measured: the text is byte-identical in text mode, only the stream differs. Matching both would mean abandoning streaming or special-casing every ported gate. So parity is redefined, and it is stronger than bytes where it counts: exit codes match exactly, no fact the old message carried is lost, and failures name a remedy as a structured field. That governs every port in T-1251, not just this one, so it is in D-263 rather than only here. test_check_parity.py runs three paths — ok, drift, missing file — through both implementations and compares. It builds a throwaway fixture repo and copies the OLD script into it, because that script resolves its root from __file__ and has no override; the new command just takes SR_REPO_ROOT. That asymmetry is part of why the port earns its keep. It also asserts the failing paths actually exit non-zero, without which "the exit codes matched" would be vacuous for two checks that both silently pass. Proven to fail twice before being trusted. Once by accident: the first version asserted the yaml version appears on every failing path, which the old script does not report when the client file is missing — the test was wrong, not the code, and it now derives expected facts from what the old output actually contains. Once on purpose: mutating the router to drop a version made it fail and name the missing fact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
619 lines
27 KiB
Makefile
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 \
|
|
validate-content check-fact-ids setup-hooks install-hooks \
|
|
audit deny atlas-verify economy-db regen-db check-systems-db check-client-version check-canvas-version \
|
|
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 check-dataflow-graph
|
|
|
|
# --- 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 check-fact-ids Check fact_id references against knowledge catalogs"
|
|
@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 check-systems-db Verify systems.db meta stamp matches current generator sources"
|
|
@echo " make check-client-version Verify client/project.godot version mirrors project.yaml"
|
|
@echo " make check-canvas-version Verify canvas-generation changes carry a version bump"
|
|
@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/check-client-version parity (T-1262)..."
|
|
@mkdir -p .cache
|
|
@$(VENV_PY) tooling/test_check_parity.py 2> .cache/test-tooling-check-parity.log || \
|
|
{ echo " FAIL: check parity — log follows:"; cat .cache/test-tooling-check-parity.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-fact-ids 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 check-fact-ids 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"
|
|
|
|
check-systems-db: ## Verify systems.db meta stamp matches current generator sources (#857)
|
|
@python3 tooling/check-systems-db-stamp --verbose
|
|
|
|
check-client-version: ## Verify client/project.godot's baked version matches project.yaml (T-1241)
|
|
@python3 tooling/check-client-version
|
|
|
|
check-canvas-version: ## Verify canvas-generation changes carry a project.yaml version bump (T-1242)
|
|
@python3 tooling/check-canvas-version
|
|
|
|
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
|
|
|
|
check-fact-ids:
|
|
@tooling/check-fact-ids
|
|
|
|
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."
|
|
|
|
# D-262: every repo path named in a hand-authored data-flow diagram must still
|
|
# resolve. Catches the stale half of a diagram; edge MEANING is a human check.
|
|
check-dataflow-graph:
|
|
@python3 tooling/check-dataflow-graph.py
|
|
|
|
# --- Clean ---
|
|
|
|
clean:
|
|
cd server && cargo clean || true
|
|
rm -rf .cache/*
|
|
rm -rf client/reports
|
|
@echo "Clean complete."
|