#!/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())