INTENT design decision 8 of 10, unimplemented for six passes. cb-sim had no flag parsing at all, so --replay had nowhere to go. The bundle is manifest + commands.log + initial.snapshot + expected.yaml, dev-only behind the scenarios feature and charged to AM-4b. The command stream goes through the K11 framing built in T05, so a truncated bundle is detected rather than replayed short — the two tasks compose rather than duplicating. The reviewer's D2 correction was real: this was not "a directory of four files". Pass carried only the end state, RunOutcome::Failed was a formatted String, and scenario.rs created an EventLog, appended to it and never read it. All three had to change. The first round trip failed to reproduce, and the cause is worth keeping: state_hash_hex over a serde_json::Value is a different canonical form than over the typed aggregate — Value's map is key-sorted, a struct serializes in declaration order. The bundle was written with one basis and verified with the other. A round trip written to recompute its own comparison value would have PASSED this bug; it failed because the recorded hash came from the producing process, which is control 2's entire purpose. make replay-test implements ADR-0005 §6's four controls, 14/14: a committed deliberately-failing fixture outside the corpus with covers: [] so it neither fails `make sim` nor inflates AM-1; a tampered recorded hash must fail; a log short by one byte and a corrupted length prefix must be rejected; and a mutated manifest seed must fail — which bites only because replay re-derives the initial state from seed+setup and checks it against the recorded snapshot, since restoring from the snapshot alone would leave the seed inert. Plus a control on the controls: the bundle must still replay after every mutation is reverted. AM-7's hash-identical clause is re-earned. The probe records a hash per per-game segment and replays each from its own genesis; folding from the wrong seed now fails. That is the clause ADR-0005 §4 withdrew as mutation-proven inert. The scaling >= 0.9x clause is still unenforced, so AM-7 stays PARTIAL — reported, not rounded up. Kernel coverage 15/18 -> 16/18. facts-check immediately caught the spec's copy of that number going stale, on a number that moved the same hour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
173 lines
6.8 KiB
Makefile
173 lines
6.8 KiB
Makefile
# One command surface (InnerLoop §agentic-efficiency #3). Deterministic,
|
|
# greppable output; precursor of the `cb` CLI.
|
|
#
|
|
# CB-WP-0004 T01: every target here runs from a clean shell, from any
|
|
# directory, with no prefix. Invoke as `make -C <repo> <target>` from
|
|
# elsewhere. No target requires `cd` or `export PATH` — CB-RES-0003
|
|
# measured 84 turns and $15.33 spent on exactly those two prefixes.
|
|
|
|
# Absolute path to this Makefile's directory, so recipes never depend on
|
|
# the caller's working directory.
|
|
REPO := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
|
|
|
# Locate cargo instead of requiring it on the inherited PATH. Mirrors
|
|
# tools/repo.py:cargo_bin() — kept in sync by `make env-test`.
|
|
CARGO := $(firstword $(shell command -v cargo 2>/dev/null) \
|
|
$(wildcard $(HOME)/.cargo/bin/cargo) \
|
|
$(wildcard /usr/local/cargo/bin/cargo) \
|
|
cargo)
|
|
export PATH := $(dir $(CARGO)):$(PATH)
|
|
|
|
PY := python3
|
|
TOOLS := $(REPO)/tools
|
|
|
|
# Every cargo recipe runs at the repo root; the shell does not persist cd.
|
|
IN_REPO := cd $(REPO) &&
|
|
|
|
.PHONY: check test sim bench bench-test coverage dep-weight cost cost-test cost-pin cost-budget cost-mix loop-lint self-tests env-test task-done status facts-check facts-gen mutation-check size-metrics runtime-metrics build-time am6 replay-test loc all
|
|
|
|
## fmt + clippy (deny warnings) + HashMap deny-lint
|
|
check:
|
|
$(IN_REPO) $(CARGO) fmt --all --check
|
|
$(IN_REPO) $(CARGO) clippy --workspace --all-targets -- -D warnings
|
|
|
|
## unit + scenario-format tests
|
|
test:
|
|
$(IN_REPO) $(CARGO) test --workspace
|
|
|
|
## run all GROUND scenarios through cb-sim
|
|
dep-weight:
|
|
$(PY) $(TOOLS)/dep-weight.py
|
|
|
|
coverage:
|
|
$(PY) $(TOOLS)/rule-coverage.py
|
|
|
|
# K10: a bundle must re-execute, and must be able to fail. Four controls
|
|
# from ADR-0005 §6, including truncate-by-one-byte and mutated-seed.
|
|
replay-test:
|
|
$(PY) $(TOOLS)/replay-test.py
|
|
|
|
# AM-6 gate. Runs in RELEASE, where headroom is ~24x; the same assertion
|
|
# in debug has ~3.4x and flaked under load. The target is unchanged — only
|
|
# where it is measured.
|
|
am6:
|
|
$(IN_REPO) $(CARGO) test --release -p games-ground --all-features \
|
|
am6_throughput -- --ignored --nocapture --test-threads=1
|
|
|
|
# AM-9 peak RSS (fast, gated). AM-5 needs a clean build — see build-time.
|
|
runtime-metrics:
|
|
$(PY) $(TOOLS)/runtime-metrics.py --fast
|
|
|
|
# AM-5: clean release build, ~90s, into a throwaway CARGO_TARGET_DIR so the
|
|
# working cache survives. Reported not gated per GameKernel §5.
|
|
build-time:
|
|
$(PY) $(TOOLS)/runtime-metrics.py
|
|
|
|
# AM-2 (M-D1-SPL, AM-1's anti-gaming pair) and AM-3.
|
|
size-metrics:
|
|
$(PY) $(TOOLS)/size-metrics.py
|
|
|
|
# M-D2-CST (specs/CostAccounting.md). cost-test is the positive control and
|
|
# runs first: a cost number from an unverified collector is void.
|
|
cost: cost-test
|
|
$(PY) $(TOOLS)/cb-cost.py --composition --by-task
|
|
|
|
cost-test:
|
|
$(PY) $(TOOLS)/cb-cost.py --self-test
|
|
|
|
# InnerLoop rules that are mechanically checkable (CB-WP-0003 T01).
|
|
loop-lint:
|
|
$(PY) $(TOOLS)/loop-lint.py
|
|
|
|
# Positive control for every reporting tool, per InnerLoop v1.1 Step 5.
|
|
self-tests:
|
|
$(PY) $(TOOLS)/cb-cost.py --self-test
|
|
$(PY) $(TOOLS)/loop-lint.py --self-test
|
|
$(PY) $(TOOLS)/rule-coverage.py --self-test
|
|
$(PY) $(TOOLS)/dep-weight.py --self-test
|
|
$(PY) $(TOOLS)/repo.py --self-test
|
|
$(PY) $(TOOLS)/task-done.py --self-test
|
|
$(PY) $(TOOLS)/status.py --self-test
|
|
$(PY) $(TOOLS)/facts.py --self-test
|
|
$(PY) $(TOOLS)/mutation-check.py --self-test
|
|
$(PY) $(TOOLS)/size-metrics.py --self-test
|
|
$(PY) $(TOOLS)/runtime-metrics.py --self-test
|
|
$(PY) $(TOOLS)/replay-test.py --self-test
|
|
|
|
# T01 positive control: prove the environment fix, do not assume it. Runs
|
|
# every tool from a foreign working directory with a PATH that has no
|
|
# cargo on it. Before T01 this failed; if it fails again, the friction is
|
|
# back and CB-EV-0003's measurement is invalid.
|
|
env-test:
|
|
@cd / && env PATH=/usr/bin:/bin $(PY) $(TOOLS)/repo.py --self-test
|
|
@cd / && env PATH=/usr/bin:/bin $(PY) $(TOOLS)/rule-coverage.py --self-test >/dev/null \
|
|
&& echo " [ok ] rule-coverage runs from / with no cargo on PATH"
|
|
@cd / && env PATH=/usr/bin:/bin $(PY) $(TOOLS)/dep-weight.py --self-test >/dev/null \
|
|
&& echo " [ok ] dep-weight runs from / with no cargo on PATH"
|
|
@cd / && env PATH=/usr/bin:/bin $(PY) $(TOOLS)/cb-cost.py --self-test >/dev/null \
|
|
&& echo " [ok ] cb-cost runs from / with no cargo on PATH"
|
|
@cd / && env PATH=/usr/bin:/bin $(PY) $(TOOLS)/loop-lint.py --self-test >/dev/null \
|
|
&& echo " [ok ] loop-lint runs from / with no cargo on PATH"
|
|
@$(MAKE) -C $(REPO) coverage >/dev/null \
|
|
&& echo " [ok ] make -C <repo> works from any directory"
|
|
|
|
# M-D1-MUT (CB-WP-0005 T02): invert each acceptance row's property and
|
|
# require the verifying command to go red. Deliberately NOT in `make all`:
|
|
# it rebuilds the workspace once per mutated row. Run it on demand and in
|
|
# CI, not in the inner loop.
|
|
mutation-check:
|
|
$(PY) $(TOOLS)/mutation-check.py $(ARGS)
|
|
|
|
# T04: single source of fact (InnerLoop v1.2) — the DFD gate.
|
|
# facts.toml is GENERATED; facts-check fails if it disagrees with the
|
|
# instruments, or if a tagged artifact disagrees with it.
|
|
facts-check:
|
|
$(PY) $(TOOLS)/facts.py --check
|
|
|
|
facts-gen:
|
|
$(PY) $(TOOLS)/facts.py --gen
|
|
|
|
# T03: one-shot orientation — workplans, next task, spend, fast gates.
|
|
# Cheap by design: no build. Start a session with this instead of grepping.
|
|
status:
|
|
@$(PY) $(TOOLS)/status.py
|
|
|
|
# T02: close a task — flip the workplan file, read the *measured* cost
|
|
# from the transcripts, push the hub event with real numbers. Refuses on an
|
|
# unknown or already-done task, and refuses to report an estimate.
|
|
# make task-done T=CB-WP-0004-T02
|
|
task-done:
|
|
@test -n "$(T)" || { echo "usage: make task-done T=CB-WP-0004-T02" >&2; exit 2; }
|
|
$(PY) $(TOOLS)/task-done.py $(T) $(ARGS)
|
|
|
|
# CB-01/CB-02: live spend since the last commit.
|
|
cost-budget: cost-test
|
|
$(PY) $(TOOLS)/cb-cost.py --budget
|
|
|
|
# CB-RES-0003 baseline: mechanical vs judgment turns.
|
|
cost-mix: cost-test
|
|
$(PY) $(TOOLS)/cb-cost.py --composition
|
|
|
|
cost-pin: cost-test
|
|
$(PY) $(TOOLS)/cb-cost.py --pin fc76445 --composition --by-task
|
|
|
|
sim:
|
|
$(IN_REPO) $(CARGO) run -q -p cb-sim -- $(REPO)/scenarios/ground/*.yaml
|
|
|
|
## Criterion benches (AM-6/AM-7)
|
|
bench:
|
|
$(IN_REPO) $(CARGO) bench -p games-ground
|
|
|
|
## InnerLoop positive control: run every bench once, no measurement.
|
|
## Fails if a workload stalls or produces the wrong event count.
|
|
bench-test:
|
|
$(IN_REPO) $(CARGO) bench -p games-ground --bench synthetic -- --test
|
|
|
|
|
|
## AM-2/AM-3 input: source LOC per crate (excludes tests would need tokei)
|
|
loc:
|
|
@$(IN_REPO) for d in crates/cb-kernel crates/cb-events crates/cb-game-runtime games/ground tools/cb-sim; do \
|
|
printf '%-28s %s\n' $$d "$$(find $$d/src -name '*.rs' | xargs cat | grep -vcE '^\s*(//|$$)')"; \
|
|
done
|
|
|
|
all: check test sim coverage size-metrics runtime-metrics am6 replay-test dep-weight self-tests env-test facts-check loop-lint bench-test
|