CB-WP-0004 T01: fix environment friction at the root
CB-RES-0003 measured 84 turns and $15.33 — the largest mechanical category — spent prefixing commands with `cd` and `export PATH="$HOME/.cargo/bin:$PATH"`. Both causes are now fixed once instead of per-leaf. tools/repo.py resolves the repo root from __file__ and cargo from PATH then the standard rustup locations. Every tool imports ROOT from it, so the repo path is stated once rather than redefined in four files — single source of fact, the rule DFD earned in InnerLoop v1.2. rule-coverage and dep-weight now call enter_root(), which is why their relative paths did not need rewriting one by one. The Makefile derives REPO from MAKEFILE_LIST and resolves CARGO the same way, so `make -C <repo> <target>` works from any directory with no prefix. make env-test is the positive control, and is in `make all`: every tool runs from / with PATH=/usr/bin:/bin. Without it this fix could regress silently and invalidate T05's measurement — the whole point of the control loop. dep-weight's "cargo not on PATH" error is kept rather than deleted. It should now be unreachable, and --self-test asserts cargo_bin() resolves unaided; a control that never fires is cheaper than a regression. loop-lint failed on repo.py on its first run — a reporting tool with a positive control but no --self-test entry point. Second time the gate has caught work from its own pass within the hour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
578dcbea78
commit
3f1dbac164
9 changed files with 254 additions and 30 deletions
83
Makefile
83
Makefile
|
|
@ -1,73 +1,112 @@
|
||||||
# One command surface (InnerLoop §agentic-efficiency #3). Deterministic,
|
# One command surface (InnerLoop §agentic-efficiency #3). Deterministic,
|
||||||
# greppable output; precursor of the `cb` CLI.
|
# 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.
|
||||||
|
|
||||||
CARGO := cargo
|
# Absolute path to this Makefile's directory, so recipes never depend on
|
||||||
|
# the caller's working directory.
|
||||||
|
REPO := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||||
|
|
||||||
.PHONY: check test sim bench bench-test coverage dep-weight cost cost-test cost-pin cost-budget cost-mix loop-lint self-tests loc all
|
# 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 loc all
|
||||||
|
|
||||||
## fmt + clippy (deny warnings) + HashMap deny-lint
|
## fmt + clippy (deny warnings) + HashMap deny-lint
|
||||||
check:
|
check:
|
||||||
$(CARGO) fmt --all --check
|
$(IN_REPO) $(CARGO) fmt --all --check
|
||||||
$(CARGO) clippy --workspace --all-targets -- -D warnings
|
$(IN_REPO) $(CARGO) clippy --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
## unit + scenario-format tests
|
## unit + scenario-format tests
|
||||||
test:
|
test:
|
||||||
$(CARGO) test --workspace
|
$(IN_REPO) $(CARGO) test --workspace
|
||||||
|
|
||||||
## run all GROUND scenarios through cb-sim
|
## run all GROUND scenarios through cb-sim
|
||||||
dep-weight:
|
dep-weight:
|
||||||
python3 tools/dep-weight.py
|
$(PY) $(TOOLS)/dep-weight.py
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
python3 tools/rule-coverage.py
|
$(PY) $(TOOLS)/rule-coverage.py
|
||||||
|
|
||||||
# M-D2-CST (specs/CostAccounting.md). cost-test is the positive control and
|
# M-D2-CST (specs/CostAccounting.md). cost-test is the positive control and
|
||||||
# runs first: a cost number from an unverified collector is void.
|
# runs first: a cost number from an unverified collector is void.
|
||||||
cost: cost-test
|
cost: cost-test
|
||||||
python3 tools/cb-cost.py --composition --by-task
|
$(PY) $(TOOLS)/cb-cost.py --composition --by-task
|
||||||
|
|
||||||
cost-test:
|
cost-test:
|
||||||
python3 tools/cb-cost.py --self-test
|
$(PY) $(TOOLS)/cb-cost.py --self-test
|
||||||
|
|
||||||
# InnerLoop rules that are mechanically checkable (CB-WP-0003 T01).
|
# InnerLoop rules that are mechanically checkable (CB-WP-0003 T01).
|
||||||
loop-lint:
|
loop-lint:
|
||||||
python3 tools/loop-lint.py
|
$(PY) $(TOOLS)/loop-lint.py
|
||||||
|
|
||||||
# Positive control for every reporting tool, per InnerLoop v1.1 Step 5.
|
# Positive control for every reporting tool, per InnerLoop v1.1 Step 5.
|
||||||
self-tests:
|
self-tests:
|
||||||
python3 tools/cb-cost.py --self-test
|
$(PY) $(TOOLS)/cb-cost.py --self-test
|
||||||
python3 tools/loop-lint.py --self-test
|
$(PY) $(TOOLS)/loop-lint.py --self-test
|
||||||
python3 tools/rule-coverage.py --self-test
|
$(PY) $(TOOLS)/rule-coverage.py --self-test
|
||||||
python3 tools/dep-weight.py --self-test
|
$(PY) $(TOOLS)/dep-weight.py --self-test
|
||||||
|
$(PY) $(TOOLS)/repo.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"
|
||||||
|
|
||||||
# CB-01/CB-02: live spend since the last commit.
|
# CB-01/CB-02: live spend since the last commit.
|
||||||
cost-budget: cost-test
|
cost-budget: cost-test
|
||||||
python3 tools/cb-cost.py --budget
|
$(PY) $(TOOLS)/cb-cost.py --budget
|
||||||
|
|
||||||
# CB-RES-0003 baseline: mechanical vs judgment turns.
|
# CB-RES-0003 baseline: mechanical vs judgment turns.
|
||||||
cost-mix: cost-test
|
cost-mix: cost-test
|
||||||
python3 tools/cb-cost.py --composition
|
$(PY) $(TOOLS)/cb-cost.py --composition
|
||||||
|
|
||||||
cost-pin: cost-test
|
cost-pin: cost-test
|
||||||
python3 tools/cb-cost.py --pin fc76445 --composition --by-task
|
$(PY) $(TOOLS)/cb-cost.py --pin fc76445 --composition --by-task
|
||||||
|
|
||||||
sim:
|
sim:
|
||||||
$(CARGO) run -q -p cb-sim -- scenarios/ground/*.yaml
|
$(IN_REPO) $(CARGO) run -q -p cb-sim -- $(REPO)/scenarios/ground/*.yaml
|
||||||
|
|
||||||
## Criterion benches (AM-6/AM-7)
|
## Criterion benches (AM-6/AM-7)
|
||||||
bench:
|
bench:
|
||||||
$(CARGO) bench -p games-ground
|
$(IN_REPO) $(CARGO) bench -p games-ground
|
||||||
|
|
||||||
## InnerLoop positive control: run every bench once, no measurement.
|
## InnerLoop positive control: run every bench once, no measurement.
|
||||||
## Fails if a workload stalls or produces the wrong event count.
|
## Fails if a workload stalls or produces the wrong event count.
|
||||||
bench-test:
|
bench-test:
|
||||||
$(CARGO) bench -p games-ground --bench synthetic -- --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)
|
## AM-2/AM-3 input: source LOC per crate (excludes tests would need tokei)
|
||||||
loc:
|
loc:
|
||||||
@for d in crates/cb-kernel crates/cb-events crates/cb-game-runtime games/ground tools/cb-sim; do \
|
@$(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*(//|$$)')"; \
|
printf '%-28s %s\n' $$d "$$(find $$d/src -name '*.rs' | xargs cat | grep -vcE '^\s*(//|$$)')"; \
|
||||||
done
|
done
|
||||||
|
|
||||||
all: check test sim coverage dep-weight self-tests loop-lint bench-test
|
all: check test sim coverage dep-weight self-tests env-test loop-lint bench-test
|
||||||
|
|
|
||||||
25
README.md
25
README.md
|
|
@ -4,4 +4,29 @@ A rebuild from scratch simulation and games engine framework set up to assimilat
|
||||||
|
|
||||||
Licensed under the Target Revenue Source License (TRSL V1C1) — see [LICENSE](LICENSE); canonical text lives in the org's `target-revenue` repository.
|
Licensed under the Target Revenue Source License (TRSL V1C1) — see [LICENSE](LICENSE); canonical text lives in the org's `target-revenue` repository.
|
||||||
|
|
||||||
|
## Running the gates
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make all # every gate, from a clean shell
|
||||||
|
make -C /path/to/clay-borg all # …or from any other directory
|
||||||
|
```
|
||||||
|
|
||||||
|
**There is no environment setup step.** No `cd`, no `export PATH`, no
|
||||||
|
activation script. `make` locates the repo from its own path and `cargo`
|
||||||
|
from the standard rustup locations; the Python tools do the same via
|
||||||
|
`tools/repo.py`. The only prerequisites are a rustup toolchain and Python
|
||||||
|
3.11+.
|
||||||
|
|
||||||
|
This is deliberate and enforced: `make env-test` runs every tool from `/`
|
||||||
|
with a PATH containing no cargo, and `make all` includes it. CB-RES-0003
|
||||||
|
measured 84 agent turns and $15.33 spent prefixing commands with `cd` and
|
||||||
|
`export PATH` before that friction was fixed at the root (CB-WP-0004 T01).
|
||||||
|
|
||||||
|
Other useful targets: `make cost` (spend per task), `make cost-budget`
|
||||||
|
(spend since the last commit), `make cost-mix` (mechanical vs judgment
|
||||||
|
turns), `make loop-lint` (executable InnerLoop rules), `make self-tests`
|
||||||
|
(every tool's positive control).
|
||||||
|
|
||||||
|
## GROUND
|
||||||
|
|
||||||
The first product vertical is a virtual tabletop implementation of **GROUND — A Game of Bonds and Rivalry: DARVO Edition**. The boardgame itself (rules, editions, content) is at home in the sister repository **`ground-game`** — that repo is authoritative for what GROUND *is*; clay-borg implements the engine that runs it.
|
The first product vertical is a virtual tabletop implementation of **GROUND — A Game of Bonds and Rivalry: DARVO Edition**. The boardgame itself (rules, editions, content) is at home in the sister repository **`ground-game`** — that repo is authoritative for what GROUND *is*; clay-borg implements the engine that runs it.
|
||||||
BIN
tools/__pycache__/repo.cpython-312.pyc
Normal file
BIN
tools/__pycache__/repo.cpython-312.pyc
Normal file
Binary file not shown.
|
|
@ -40,7 +40,8 @@ except ModuleNotFoundError: # pragma: no cover - py<3.11
|
||||||
print("ERROR: needs Python 3.11+ for tomllib", file=sys.stderr)
|
print("ERROR: needs Python 3.11+ for tomllib", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
from repo import ROOT as REPO # noqa: E402 (single source of fact, T01)
|
||||||
|
|
||||||
PRICES = os.path.join(REPO, "benchmarks", "baselines", "model-prices.toml")
|
PRICES = os.path.join(REPO, "benchmarks", "baselines", "model-prices.toml")
|
||||||
COMPONENTS = ("input", "output", "cache_read", "write_5m", "write_1h")
|
COMPONENTS = ("input", "output", "cache_read", "write_5m", "write_1h")
|
||||||
TASK_RE = re.compile(r"\bT\d\d\b")
|
TASK_RE = re.compile(r"\bT\d\d\b")
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ Usage: python3 tools/dep-weight.py [--json] [--self-test]
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from repo import cargo_bin, enter_root
|
||||||
|
|
||||||
PACKAGE = "games-ground"
|
PACKAGE = "games-ground"
|
||||||
CONFIGS = {
|
CONFIGS = {
|
||||||
"shipped-runtime": ["--no-default-features"],
|
"shipped-runtime": ["--no-default-features"],
|
||||||
|
|
@ -42,14 +43,19 @@ TARGETS = {
|
||||||
|
|
||||||
def crates(extra_args):
|
def crates(extra_args):
|
||||||
"""Third-party crates in the normal (non-dev) dependency graph."""
|
"""Third-party crates in the normal (non-dev) dependency graph."""
|
||||||
if not shutil.which("cargo"):
|
# T01: locate cargo rather than demanding the caller export PATH. The
|
||||||
|
# error below is kept as a positive control — it should now be
|
||||||
|
# unreachable on a machine with rustup installed, and a control that
|
||||||
|
# never fires is still cheaper than a regression.
|
||||||
|
cargo = cargo_bin()
|
||||||
|
if not cargo:
|
||||||
print(
|
print(
|
||||||
"ERROR: cargo not on PATH. Try: export PATH=\"$HOME/.cargo/bin:$PATH\"",
|
"ERROR: cargo not found on PATH or in ~/.cargo/bin — is rustup installed?",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
out = subprocess.run(
|
out = subprocess.run(
|
||||||
["cargo", "tree", "-p", PACKAGE, "--edges", "normal", "--prefix", "none"]
|
[cargo, "tree", "-p", PACKAGE, "--edges", "normal", "--prefix", "none"]
|
||||||
+ extra_args,
|
+ extra_args,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
|
@ -114,6 +120,11 @@ def self_test():
|
||||||
|
|
||||||
# Targets must be present and numeric — a missing target would make
|
# Targets must be present and numeric — a missing target would make
|
||||||
# the breach check vacuous.
|
# the breach check vacuous.
|
||||||
|
# T01: this tool is useless without cargo, and used to demand the caller
|
||||||
|
# put it on PATH. Assert it resolves unaided.
|
||||||
|
check("cargo resolves without caller PATH setup", bool(cargo_bin()),
|
||||||
|
cargo_bin() or "NOT FOUND")
|
||||||
|
|
||||||
check("targets defined for every configuration",
|
check("targets defined for every configuration",
|
||||||
set(TARGETS) == set(CONFIGS) and all(
|
set(TARGETS) == set(CONFIGS) and all(
|
||||||
isinstance(v, int) and v > 0 for v in TARGETS.values()),
|
isinstance(v, int) and v > 0 for v in TARGETS.values()),
|
||||||
|
|
@ -129,6 +140,8 @@ def self_test():
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# T01: `cargo tree` and the own-source walk are both repo-relative.
|
||||||
|
enter_root()
|
||||||
if "--self-test" in sys.argv:
|
if "--self-test" in sys.argv:
|
||||||
return self_test()
|
return self_test()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
from repo import ROOT as REPO # noqa: E402 (single source of fact, T01)
|
||||||
LOADABILITY_LIMIT = 400
|
LOADABILITY_LIMIT = 400
|
||||||
|
|
||||||
# Artifact classes the loop produces. history/ is an append-only trail
|
# Artifact classes the loop produces. history/ is an append-only trail
|
||||||
|
|
|
||||||
124
tools/repo.py
Normal file
124
tools/repo.py
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Repo location and toolchain discovery — the root fix for CB-WP-0004 T01.
|
||||||
|
|
||||||
|
CB-RES-0003 measured 84 turns and $15.33 spent on `cd` and
|
||||||
|
`export PATH="$HOME/.cargo/bin:$PATH"`. That friction had two causes:
|
||||||
|
|
||||||
|
1. tools resolved their inputs relative to the *caller's* working
|
||||||
|
directory, so every invocation had to be preceded by a `cd`;
|
||||||
|
2. `cargo` is not on the default PATH, so every Rust-touching command had
|
||||||
|
to be preceded by an `export`.
|
||||||
|
|
||||||
|
Both are fixed here, once, rather than at each leaf. `tools/dep-weight.py`
|
||||||
|
previously carried its own "cargo not on PATH" message — evidence the
|
||||||
|
friction was noticed and patched in the wrong place.
|
||||||
|
|
||||||
|
Single source of fact (InnerLoop v1.2): ROOT is derived here and imported;
|
||||||
|
it is not recomputed per tool.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# tools/repo.py -> tools -> repo root.
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
# Where a rustup install puts the toolchain when PATH has not been set up.
|
||||||
|
CARGO_FALLBACKS = (
|
||||||
|
os.path.expanduser("~/.cargo/bin"),
|
||||||
|
"/usr/local/cargo/bin",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enter_root():
|
||||||
|
"""Run from the repo root regardless of where the caller invoked us.
|
||||||
|
|
||||||
|
Every tool calls this first. After it, plain relative paths
|
||||||
|
(`specs/...`, `scenarios/...`) are correct, which is why the tools
|
||||||
|
below did not need rewriting path-by-path.
|
||||||
|
"""
|
||||||
|
os.chdir(ROOT)
|
||||||
|
return ROOT
|
||||||
|
|
||||||
|
|
||||||
|
def cargo_bin():
|
||||||
|
"""Absolute path to cargo, or None.
|
||||||
|
|
||||||
|
Checks PATH first so an explicitly-configured toolchain wins, then the
|
||||||
|
standard rustup locations. Returning None rather than exiting keeps the
|
||||||
|
policy (abort? skip?) with the caller.
|
||||||
|
"""
|
||||||
|
found = shutil.which("cargo")
|
||||||
|
if found:
|
||||||
|
return found
|
||||||
|
for d in CARGO_FALLBACKS:
|
||||||
|
cand = os.path.join(d, "cargo")
|
||||||
|
if os.path.isfile(cand) and os.access(cand, os.X_OK):
|
||||||
|
return cand
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def cargo_env():
|
||||||
|
"""Environment with the cargo bin dir prepended to PATH.
|
||||||
|
|
||||||
|
For subprocesses that shell out to cargo indirectly. Returns a copy;
|
||||||
|
never mutates os.environ.
|
||||||
|
"""
|
||||||
|
env = dict(os.environ)
|
||||||
|
cargo = cargo_bin()
|
||||||
|
if cargo:
|
||||||
|
env["PATH"] = os.path.dirname(cargo) + os.pathsep + env.get("PATH", "")
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def self_test():
|
||||||
|
"""Positive control: this module must actually locate things.
|
||||||
|
|
||||||
|
A resolver that silently returns a wrong-but-plausible answer is the
|
||||||
|
harness-does-nothing class applied to paths — every downstream tool
|
||||||
|
would then measure the wrong tree while reporting success.
|
||||||
|
"""
|
||||||
|
results = []
|
||||||
|
|
||||||
|
def check(name, ok, detail=""):
|
||||||
|
results.append((name, ok, detail))
|
||||||
|
|
||||||
|
check("ROOT is a directory", os.path.isdir(ROOT), ROOT)
|
||||||
|
# Identified by content, not by name: a ROOT that pointed at the parent
|
||||||
|
# directory would still be "a directory".
|
||||||
|
check("ROOT is *this* repo",
|
||||||
|
os.path.isfile(os.path.join(ROOT, "Cargo.toml"))
|
||||||
|
and os.path.isdir(os.path.join(ROOT, "specs"))
|
||||||
|
and os.path.isfile(os.path.join(ROOT, "INTENT.md")))
|
||||||
|
# ROOT must not depend on the caller's cwd — the defect this fixes.
|
||||||
|
here = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir("/")
|
||||||
|
again = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
check("ROOT is independent of the caller's cwd", again == ROOT)
|
||||||
|
finally:
|
||||||
|
os.chdir(here)
|
||||||
|
|
||||||
|
cargo = cargo_bin()
|
||||||
|
check("cargo is locatable without PATH setup", bool(cargo), cargo or "NOT FOUND")
|
||||||
|
check("cargo_env puts cargo on PATH",
|
||||||
|
not cargo or os.path.dirname(cargo) in cargo_env()["PATH"].split(os.pathsep))
|
||||||
|
|
||||||
|
print("repo self-test (positive control)")
|
||||||
|
ok = True
|
||||||
|
for name, passed, detail in results:
|
||||||
|
print(f" [{'ok ' if passed else 'FAIL'}] {name}"
|
||||||
|
+ (f" — {detail}" if detail else ""))
|
||||||
|
ok &= passed
|
||||||
|
return 0 if ok else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# This module has nothing to report but its own controls, so bare
|
||||||
|
# invocation and --self-test are the same run. The flag exists because
|
||||||
|
# `make self-tests` and loop-lint both look for it by name.
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] not in ("--self-test",):
|
||||||
|
print(f"usage: {sys.argv[0]} [--self-test]", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
sys.exit(self_test())
|
||||||
|
|
@ -24,6 +24,8 @@ import glob
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from repo import enter_root
|
||||||
|
|
||||||
RULE_RE = r"\*\*(GR-[A-Z]+\d+)"
|
RULE_RE = r"\*\*(GR-[A-Z]+\d+)"
|
||||||
COVERS_RE = r"covers: \[(.*?)\]"
|
COVERS_RE = r"covers: \[(.*?)\]"
|
||||||
AGGREGATE = "games/ground/src/lib.rs"
|
AGGREGATE = "games/ground/src/lib.rs"
|
||||||
|
|
@ -107,6 +109,9 @@ def self_test():
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# T01: inputs are repo-relative, so anchor to the repo rather than
|
||||||
|
# requiring the caller to `cd` first.
|
||||||
|
enter_root()
|
||||||
if "--self-test" in sys.argv:
|
if "--self-test" in sys.argv:
|
||||||
return self_test()
|
return self_test()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
id: CB-WP-0004
|
id: CB-WP-0004
|
||||||
title: "Move mechanical turns off the token budget, and prove it worked"
|
title: "Move mechanical turns off the token budget, and prove it worked"
|
||||||
status: proposed
|
status: in_progress
|
||||||
state_hub_workstream_id: "6880ac78-d817-41b9-b267-f12ff9deea28"
|
state_hub_workstream_id: "6880ac78-d817-41b9-b267-f12ff9deea28"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ unless the instrument disproved it (§Step 4, correction vs retarget).
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CB-WP-0004-T01
|
id: CB-WP-0004-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "3ddfd3e2-8596-4969-a069-09577933fcbc"
|
state_hub_task_id: "3ddfd3e2-8596-4969-a069-09577933fcbc"
|
||||||
```
|
```
|
||||||
|
|
@ -64,6 +64,23 @@ still cheaper than a regression.
|
||||||
**Predicted:** environment-setup turns → **< 10** (from 84),
|
**Predicted:** environment-setup turns → **< 10** (from 84),
|
||||||
**$12–15** recovered. Highest confidence in the review.
|
**$12–15** recovered. Highest confidence in the review.
|
||||||
|
|
||||||
|
**Delivered.** `tools/repo.py` resolves the repo root from `__file__` and
|
||||||
|
`cargo` from PATH-then-rustup-locations; every tool imports it, so `REPO`
|
||||||
|
is now stated once rather than four times. The Makefile derives `REPO`
|
||||||
|
from `MAKEFILE_LIST` and resolves `CARGO` the same way. `make env-test`
|
||||||
|
is the positive control — it runs every tool from `/` with
|
||||||
|
`PATH=/usr/bin:/bin`, and is wired into `make all`, so this cannot
|
||||||
|
silently regress and invalidate T05's measurement.
|
||||||
|
|
||||||
|
The leaf workaround in `dep-weight.py` was kept, not deleted: it is now
|
||||||
|
unreachable on a rustup machine, and `--self-test` asserts `cargo_bin()`
|
||||||
|
resolves unaided. Per the task text, a control that never fires is
|
||||||
|
cheaper than a regression.
|
||||||
|
|
||||||
|
`loop-lint` failed on `repo.py` immediately — a new tool with a positive
|
||||||
|
control but no `--self-test` entry point. Second time the gate has caught
|
||||||
|
its own pass's work within the hour.
|
||||||
|
|
||||||
## Task: `make task-done` — one command for a task close
|
## Task: `make task-done` — one command for a task close
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue