diff --git a/wizard-work/forge/TRIAGE_DUMP_REFACTOR_VERIFICATION.md b/wizard-work/forge/TRIAGE_DUMP_REFACTOR_VERIFICATION.md index d095e7e..5eea874 100644 --- a/wizard-work/forge/TRIAGE_DUMP_REFACTOR_VERIFICATION.md +++ b/wizard-work/forge/TRIAGE_DUMP_REFACTOR_VERIFICATION.md @@ -2,7 +2,7 @@ **Lane:** Refactor — code review and refactors **Artifact committed to:** `wizard-work/forge/` (`_triage_dump.py`, `_triage_dump_refactored.py`, `verify_triage_dump_refactor.py`, `_triage_dump_refactor_CHECK_EVIDENCE.txt`) -**Verdict:** 11/11 checks pass; probe output byte-identical to the original (6695 bytes, sha256 `1bd715fb…`). +**Verdict:** 14/14 checks pass; probe output byte-identical to the original (6695 bytes, sha256 `1bd715fb…`). ## Target @@ -12,8 +12,8 @@ triage inbox seed `inbox/seed-2026-09-26.json`. The original ran its **entire** probe at module top level: it opened the JSON, loaded it into the module-global `d`, and printed everything at import time, with no `if __name__ == "__main__"` guard, **zero function definitions**, two module-level `for` loops, a hardcoded -JSON path at module scope, and the module-global `d` coupling data flow to the -import side effect. +host-absolute JSON path at module scope, and the module-global `d` coupling data +flow to the import side effect. ## Smells fixed (behavior-preserving) @@ -22,12 +22,16 @@ import side effect. explicitly guarded with `if __name__ == "__main__": main()`. - *Measured:* `check_import_side_effect_free` — importing produces no stdout/ stderr and no `SystemExit`. -2. **Hardcoded path at module scope.** The bare `path = "/root/.../seed...json"` - assignment is promoted to the named constant `DEFAULT_PATH` and made the - `path` parameter of `main()`. Callers/tests can override it without editing - source; the default is unchanged, so output is byte-identical when run with no - args. - - *Measured:* `check_main_path_parameter` — `main` AST has a `path` arg. +2. **Hardcoded host-absolute path at module scope (Q86).** The bare + `path = "/root/troupe-standup/inbox/seed-...json"` literal is replaced by a + tree-relative resolution (`_HERE` -> repo root -> `inbox/seed-...json`) + promoted to the named constant `DEFAULT_PATH` and the `path` parameter of + `main()`. On this host the resolution is identical to the old literal + (output byte-identical), but a clone now grades *its own* inbox instead of + silently grading `/root/...` on the host. + - *Measured:* `check_main_path_parameter`; `check_seed_path_portable` — no + host-absolute literal in the refactored source and `DEFAULT_PATH` resolves + inside the tree. 3. **No function extraction / module-global coupling.** The two inline print loops (decided, pending) are extracted into the pure helpers `_print_decided` and `_print_pending` (plus `load_data`); `d.get("decided", [])` and @@ -40,7 +44,7 @@ import side effect. ## Verification harness -`verify_triage_dump_refactor.py` — 11 checks, each a real subprocess/AST +`verify_triage_dump_refactor.py` — 14 checks, each a real subprocess/AST assertion (no tautology): | # | check | what it proves | @@ -54,8 +58,11 @@ assertion (no tautology): | 7 | main delegation | `main()` body has no `;`-compacted statements (extraction boundary intact) | | 8 | `main(path)` param | `main` accepts a configurable `path` parameter | | 9 | no dead imports | `json` retained and referenced; no unreferenced imports introduced | -| 10 | original still smells | non-vacuous: original still lacks guard, still hardcodes the path, still has module-level `for`, still has zero function defs | +| 10 | original still smells | non-vacuous: original still lacks guard, still hardcodes the host-absolute path, still has module-level `for`, still has zero function defs | | 11 | path override honoured | `main(path=custom)` prints the custom file, not `DEFAULT_PATH` — kills the "accepts-but-ignores-path" mutation, which is byte-identical on the real seed and thus evades check 2 | +| 12 | no import-time I/O (M6 killer) | M6 (`_bootstrap = json.load(open(DEFAULT_PATH))` at module scope, no print) survives check 5 by being *silent*, not clean. Static AST walk of top-level statements + dynamic `builtins.open` spy both require zero calls. | +| 13 | M6 mutant detected (non-vacuous) | The check-12 detector fires on the measured M6 mutation applied to a temp copy, so it is sensitive rather than theater. | +| 14 | seed path portable (Q86) | No host-absolute seed literal in the refactored source; `DEFAULT_PATH` resolves inside the tree and matches the harness's `REAL_SEED_PATH`. Closes the "a clone grades the host's inbox" gap. | ## Evidence (measured, this run) @@ -72,8 +79,11 @@ OK: main(path) accepts a configurable path parameter OK: all refactored imports are referenced (json retained, no dead imports) OK: original still exhibits the removed smells (refactor is non-vacuous) OK: main(path=custom) honours the override (prints custom file, not DEFAULT_PATH) +OK: no import-time I/O (0 module-scope io calls; 0 runtime open calls) +OK: M6 mutant detected at module scope: [(51, 'json.load'), (51, 'open')] +OK: seed path is tree-portable (resolves inside tree): /root/troupe-standup/inbox/seed-2026-09-26.json -=== Verification: 11/11 checks passed === (exit 0) +=== Verification: 14/14 checks passed === ``` Re-run under the optimised interpreter (per the lane's "python3 and -O" evidence @@ -81,9 +91,7 @@ convention): ``` $ python3 -O verify_triage_dump_refactor.py -OK: main(path=custom) honours the override (prints custom file, not DEFAULT_PATH) - -=== Verification: 11/11 checks passed === (exit 0) +=== Verification: 14/14 checks passed === ``` Full capture in `wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt`. @@ -92,9 +100,9 @@ Full capture in `wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt`. Check 10 guarantees the refactor actually removed something: the *original* `_triage_dump.py` still exhibits every smell the refactored version no longer -does (no `__main__` guard, hardcoded path at module scope, two module-level -`for` loops, zero function definitions, module-global `d`). A no-op "refactor" -would fail this. +does (no `__main__` guard, hardcoded host-absolute path at module scope, two +module-level `for` loops, zero function definitions, module-global `d`). A no-op +"refactor" would fail this. ``` $ python3 - <<'PY' @@ -111,6 +119,12 @@ main-guard count -> 0 module-level for -> 2 ``` +Checks 12+13 are a paired non-vacuity pair for the import-time-IO detector: +the clean module has zero module-scope io calls (check 12), and the M6 mutant +(a silent module-scope `json.load(open(DEFAULT_PATH))`) is detected by the same +detector (check 13). A detector that matches nothing would be theater; M6 proves +check 12 is surgical. + ## Mutation kill Check 11 (path override) is the key discriminator the byte-comparison cannot @@ -119,15 +133,18 @@ see: a `path` parameter that is accepted but ignored (always reads wrong data on a custom file. The check feeds a synthetic JSON containing `"overridden"` and asserts that token appears and the real seed path does not. -Check 10 (original still smells) is the structural discriminator: re-adding any -of the removed smells to the refactored file is caught by checks 3/8/6, and a -no-op refactor fails check 10 outright. +Checks 3/8/6/12 form the structural discriminators: re-adding any of the removed +smells to the refactored file is caught, and a no-op refactor fails check 10 +outright. ## Independent byte-identical proof (outside the harness) +`_triage_dump.py` (the in-tree original; a byte-identical copy of the repo-root +throwaway `.triage_dump.py`) vs. `_triage_dump_refactored.py`: + ``` -$ python3 .triage_dump.py 1>/tmp/oout 2>/tmp/oerr -$ python3 wizard-work/forge/_triage_dump_refactored.py 1>/tmp/rout 2>/tmp/rerr +$ python3 _triage_dump.py 1>/tmp/oout 2>/tmp/oerr +$ python3 _triage_dump_refactored.py 1>/tmp/rout 2>/tmp/rerr $ sha256sum /tmp/oout /tmp/rout 1bd715fbd97a6fc25a4c43581b20c231981118408a61689b4a9cd281cc3cc25b /tmp/oout 1bd715fbd97a6fc25a4c43581b20c231981118408a61689b4a9cd281cc3cc25b /tmp/rout @@ -137,6 +154,8 @@ $ cmp /tmp/oerr /tmp/rerr && echo STDERR_IDENTICAL STDERR_IDENTICAL (cmp exit 0) ``` -VERDICT: PASS -- triage_dump probe refactor is behavior-preserving; byte-identical output (6695 bytes, sha256 `1bd715fb…`), 11/11 on python3 and -O, exit 0 +VERDICT: PASS -- triage_dump probe refactor is behavior-preserving; byte-identical +output (6695 bytes, sha256 `1bd715fb…`), 14/14 on python3 and -O, exit 0; Q86 +gaps closed (import-time I/O detected, seed path tree-portable). -CHECK: python3 /root/troupe-standup/wizard-work/forge/verify_triage_dump_refactor.py >/dev/null 2>&1 +CHECK: python3 /root/troupe-standup/wizard-work/forge/verify_triage_dump_refactor.py >/dev/null 2>&1 \ No newline at end of file diff --git a/wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt b/wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt index c73b5b4..73a3090 100644 --- a/wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt +++ b/wizard-work/forge/_triage_dump_refactor_CHECK_EVIDENCE.txt @@ -1,3 +1,4 @@ +$ python3 verify_triage_dump_refactor.py OK: py_compile passes OK: identical output (exit 0, 6695 bytes) OK: __main__ guard present @@ -9,5 +10,28 @@ OK: main(path) accepts a configurable path parameter OK: all refactored imports are referenced (json retained, no dead imports) OK: original still exhibits the removed smells (refactor is non-vacuous) OK: main(path=custom) honours the override (prints custom file, not DEFAULT_PATH) +OK: no import-time I/O (0 module-scope io calls; 0 runtime open calls) +OK: M6 mutant detected at module scope: [(51, 'json.load'), (51, 'open')] +OK: seed path is tree-portable (resolves inside tree): /root/troupe-standup/inbox/seed-2026-09-26.json -=== Verification: 11/11 checks passed === +=== Verification: 14/14 checks passed === + +$ python3 -O verify_triage_dump_refactor.py +OK: py_compile passes +OK: identical output (exit 0, 6695 bytes) +OK: __main__ guard present +OK: no 'global' statements in code +OK: import is side-effect-free (no probe output at import) +OK: _print_decided, _print_pending, load_data, main extracted; main calls both print helpers +OK: main() delegates via distinct standalone statements (no ';'-compaction) +OK: main(path) accepts a configurable path parameter +OK: all refactored imports are referenced (json retained, no dead imports) +OK: original still exhibits the removed smells (refactor is non-vacuous) +OK: main(path=custom) honours the override (prints custom file, not DEFAULT_PATH) +OK: no import-time I/O (0 module-scope io calls; 0 runtime open calls) +OK: M6 mutant detected at module scope: [(51, 'json.load'), (51, 'open')] +OK: seed path is tree-portable (resolves inside tree): /root/troupe-standup/inbox/seed-2026-09-26.json + +=== Verification: 14/14 checks passed === + +exit codes: plain=0 optimised=0 diff --git a/wizard-work/forge/_triage_dump_refactored.py b/wizard-work/forge/_triage_dump_refactored.py index 5885392..1ad27f0 100644 --- a/wizard-work/forge/_triage_dump_refactored.py +++ b/wizard-work/forge/_triage_dump_refactored.py @@ -2,10 +2,11 @@ """Triage dump probe (refactored, forge-lane Refactor). Refactored from ``_triage_dump.py`` (a byte-identical copy of the repo-root -``.triage_dump.py``). The original ran its entire probe at **module top -level** -- opening the JSON, loading it into the module-global ``d``, and -printing all output at import time, with no ``if __name__ == "__main__"`` guard. -Importing it executed the probe and wrote to stdout. +throwaway probe ``.triage_dump.py``). The original ran its entire probe at +**module top level** -- opening the JSON, loading it into the module-global +``d``, and printing all output at import time, with no +``if __name__ == "__main__"`` guard. Importing it executed the probe and +wrote to stdout. Smells fixed in this refactor (behavior-preserving on the probe itself): @@ -14,17 +15,20 @@ Smells fixed in this refactor (behavior-preserving on the probe itself): entry point is explicitly guarded. ``import _triage_dump_refactored`` produces no output and does not terminate the process. - * **Hardcoded path at module scope.** The JSON path was a bare module-level - assignment with no override. It is promoted to the named constant - ``DEFAULT_PATH`` and made the ``path`` parameter of ``main()``, so callers - (including tests) can override it without editing source. The default is - unchanged, so output is byte-identical when run with no args. + * **Hardcoded host-absolute path at module scope.** The JSON path was a + bare ``/root/.../inbox/seed-...json`` literal with no override, so a clone + graded *this host's* inbox. It is now resolved from this file's location + (walked up to the repo root + ``inbox/seed-...json``) and promoted to the + named constant ``DEFAULT_PATH`` / the ``path`` parameter of ``main()``, so + callers (including tests) can override it. On this host the resolution is + identical to the old literal, so output stays byte-identical; on a clone it + resolves into the clone's tree. See Q86. * **No function extraction / module-global coupling.** The two inline print loops (decided, pending) are extracted into the pure formatting helpers ``_print_decided`` and ``_print_pending`` that take their data as arguments rather than reading the module-global ``d``. The module-global ``d`` is - removed: data flows from ``main`` into the helpers as parameters. + gone: data flows from ``main`` into the helpers as parameters. Output is byte-identical to the original on the current inbox seed (``inbox/seed-2026-09-26.json``): 6695 bytes, sha256 @@ -33,8 +37,15 @@ Output is byte-identical to the original on the current inbox seed from __future__ import annotations import json +import os -DEFAULT_PATH = "/root/troupe-standup/inbox/seed-2026-09-26.json" +# Resolve the seed from THIS file's location so the probe is self-contained: +# it grades the tree it lives in, not this host's inbox (Q86 portability gap). +# wizard-work/forge/ is two levels under the repo root, which holds inbox/. +_HERE = os.path.dirname(os.path.abspath(__file__)) +DEFAULT_PATH = os.path.join( + os.path.dirname(os.path.dirname(_HERE)), "inbox", "seed-2026-09-26.json" +) def load_data(path): diff --git a/wizard-work/forge/verify_triage_dump_refactor.py b/wizard-work/forge/verify_triage_dump_refactor.py index b27c007..2ce269b 100644 --- a/wizard-work/forge/verify_triage_dump_refactor.py +++ b/wizard-work/forge/verify_triage_dump_refactor.py @@ -36,8 +36,20 @@ Checks: for-loops, zero function definitions, runs at import). 11. ``main(path)`` honours the override: calling with a custom temp JSON path prints the custom file's data and not the real seed path. This kills the - mutation of a ``path`` parameter that is accepted but ignored (byte-identical - on the real seed would not catch this). + mutation of a ``path`` parameter that is accepted but ignored (byte- + identical on the real seed would not catch this). + 12. No import-time I/O (M6 killer): a static AST walk of the top-level + statements plus a runtime ``builtins.open`` spy both require zero calls. + Mutant M6 (``_bootstrap = json.load(open(DEFAULT_PATH))`` at module scope, + no print) survived check 5 (rc 0, zero output) by being *silent*, not + clean -- the read happened but was invisible to the silence test. + 13. M6 is detected by check 12's io-detector (non-vacuity): the detector fires + on the measured mutant applied to a temp copy, so it is sensitive rather + than theater. + 14. The seed path is tree-portable: no host-absolute literal in the refactored + source, and ``DEFAULT_PATH`` resolves inside the tree. Closes the Q86 + "a clone grades the host's inbox" gap -- on this host the resolution is + identical to the old literal, so check 2 stays byte-identical. """ from __future__ import annotations @@ -58,7 +70,17 @@ WORK_DIR = os.path.dirname(os.path.abspath(__file__)) ORIGINAL = "_triage_dump.py" REFACTORED = "_triage_dump_refactored.py" MOD_NAME = REFACTORED[:-3] # strip .py -> _triage_dump_refactored -REAL_SEED_PATH = "/root/troupe-standup/inbox/seed-2026-09-26.json" +SEED_FILENAME = "seed-2026-09-26.json" +# The original probe's literal -- used only to assert the ORIGINAL still carries +# the host-absolute smell (clone-independent, unlike comparing against a resolved +# real-seed path). +HOST_SEED_LITERAL = "/root/troupe-standup/inbox/seed-2026-09-26.json" +# The seed is derived from THIS file's location (walked up to the repo root), +# not a host-absolute literal, so a clone grades its own inbox (Q86). On this +# host the resolution is identical to the old literal -> check 2 stays green. +_VERIFY_DIR = os.path.dirname(os.path.abspath(__file__)) +REAL_SEED_PATH = os.path.join( + os.path.dirname(os.path.dirname(_VERIFY_DIR)), "inbox", SEED_FILENAME) def _run(*args: str) -> subprocess.CompletedProcess: @@ -295,15 +317,35 @@ def check_no_dead_imports() -> bool: return True +def _original_hardcodes_absolute_seed(src: str) -> bool: + """True if the original assigns an absolute path literal to ``path`` at + module scope -- the host-absolute smell the refactor removes. + + AST-based so the test is clone-independent: the original's literal is + host-absolute by design (``/root/.../inbox/seed-....json``), which would + break a naive ``REAL_SEED_PATH in code`` test on any checkout whose + REAL_SEED_PATH is tree-relative (i.e. everywhere, after this fix).""" + tree = ast.parse(src) + for stmt in tree.body: + if (isinstance(stmt, ast.Assign) + and isinstance(stmt.value, ast.Constant) + and isinstance(stmt.value.value, str) + and stmt.value.value.startswith("/")): + for t in stmt.targets: + if isinstance(t, ast.Name) and t.id == "path": + return True + return False + + def check_original_still_smells() -> bool: """The original must still carry the smells we removed (non-vacuous).""" src = _read(ORIGINAL) - code = _code_only(src) problems = [] if _guard_present(src): problems.append("original already has __main__ guard (refactor not needed)") - if REAL_SEED_PATH not in code: - problems.append("hardcoded path missing from original (expected)") + if not _original_hardcodes_absolute_seed(src): + problems.append("module-scope absolute `path = ` missing from " + "original (expected)") tree = ast.parse(src) has_module_level_for = any(isinstance(n, ast.For) for n in tree.body) if not has_module_level_for: @@ -366,6 +408,160 @@ def check_path_override_honoured() -> bool: shutil.rmtree(tmp_dir, ignore_errors=True) +# ---------------------------------------------------------------------- +# Q86: the refactor-lane harness proved SILENCE at import, not ABSENCE of I/O, +# and its seed path was host-absolute. The two fixes below close both gaps. +# +# A Call to any of these at MODULE SCOPE runs while the module is imported -- +# the M6 mutant (`_bootstrap = json.load(open(DEFAULT_PATH))`, no print) hides +# from check 5 because it emits no stdout, so it needs its own detector. +_IO_CALLS = frozenset({ + "open", "json.load", "json.loads", "pickle.load", "pickle.loads", + "csv.reader", "csv.DictReader", "urllib.request.urlopen", "codecs.open", +}) + + +def _call_name(call: ast.Call): + """Dotted name of a Call's function (``json.load``, ``open``, ...), or + None for a callable expression this detector does not model.""" + parts = [] + cur = call.func + while isinstance(cur, ast.Attribute): + parts.append(cur.attr) + cur = cur.value + if isinstance(cur, ast.Name): + parts.append(cur.id) + return ".".join(reversed(parts)) + return None + + +def _calls_at_module_scope(src: str) -> list: + """``[(lineno, name)]`` for io-reading Call nodes at module scope. + + A Call counts as "module scope" only when it is NOT nested inside a + FunctionDef / AsyncFunctionDef / ClassDef body -- those run lazily, at call + time, and are exactly the I/O the refactor is allowed to keep (it lives + under ``main()``). The ``if __name__ == "__main__"`` guard's ``main()`` + call resolves to ``main`` (not in the io set), so it is correctly ignored. + """ + tree = ast.parse(src) + found: list = [] + + def scan(node): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + return # do not descend -- calls inside a def body are lazy + if isinstance(node, ast.Call): + name = _call_name(node) + if name in _IO_CALLS: + found.append((node.lineno, name)) + for child in ast.iter_child_nodes(node): + scan(child) + + for stmt in tree.body: + scan(stmt) + return found + + +def check_no_import_time_io() -> bool: + """Check 5 proves the import is SILENT, not that no I/O happens. M6 + (`_bootstrap = json.load(open(DEFAULT_PATH))` at module scope, no print) + survives check 5 (rc 0, zero output) while reading the seed at import. + Two independent detectors, both must read zero on the clean module: + (a) static: AST-walk the top-level statements for an open/json.load call; + (b) dynamic: patch builtins.open to a recorder, re-import the module, and + require zero calls. The import machinery reads .py source via + _io.FileIO (not builtins.open), so this does NOT false-positive on a + clean module -- measured separately (0 calls on a fresh import).""" + src = _read(REFACTORED) + static = _calls_at_module_scope(src) + if static: + print("FAIL: static io-call(s) at module scope: %s" % static) + return False + # Dynamic backstop: require zero open() calls during a fresh import. + import builtins + calls: list = [] + real_open = builtins.open + + def _spy_open(*args, **kwargs): + calls.append(args[0] if args else kwargs.get("file")) + raise RuntimeError("__check_no_import_time_io__ import-time open()") + + builtins.open = _spy_open + try: + _mod, exc, _out, _err = _safe_import() + finally: + builtins.open = real_open + if exc is not None: + # A RuntimeError carrying our marker is the very failure we reject; + # any other exception is a real import error and is ungradable here. + if isinstance(exc, RuntimeError) and "__check_no_import_time_io__" in str(exc): + print("FAIL: runtime import-time open() on %r (rc 2)" % calls) + return False + print("FAIL: import raised %s: %r (stdout=%r stderr=%r)" + % (type(exc).__name__, exc, _out, _err)) + return False + if calls: + print("FAIL: import made %d open() call(s): %r" % (len(calls), calls)) + return False + print("OK: no import-time I/O (0 module-scope io calls; 0 runtime open calls)") + return True + + +def check_m6_mutant_killed() -> bool: + """Non-vacuity for check 12: a detector that matches nothing is theater. + Apply the measured M6 mutation -- a silent module-scope + ``json.load(open(DEFAULT_PATH))`` inserted before the first def -- to a temp + copy of the source and confirm the static io-detector fires on it. The + clean module has zero such calls (check 12); the mutant has two, so the + detector is sensitive rather than vacuous. (M6 is the mutant measured for + Q86: it returns 11/11 under the old check 5.)""" + src = _read(REFACTORED) + m6_expr = "_bootstrap = json.load(open(DEFAULT_PATH))\n" + mutated = src.replace("def load_data(path):", + m6_expr + "def load_data(path):", 1) + if m6_expr not in mutated: + # guard against the insertion anchor drifting in the source + print("FAIL: M6 mutation could not be applied (anchor 'def load_data' not found)") + return False + sites = _calls_at_module_scope(mutated) + if not sites: + print("FAIL: M6 mutant NOT detected by the io-detector (check 12 is vacuous?)") + return False + print("OK: M6 mutant detected at module scope: %s" % sites) + return True + + +def check_seed_path_portable() -> bool: + """Q86 gap #2: the seed path must derive from the tree, not a host-absolute + literal, so a clone grades its own inbox. Measured against two claims: + (a) the refactored source holds no host-absolute seed literal; + (b) importing the module, its DEFAULT_PATH resolves to a path INSIDE this + file's tree, and that resolution matches the harness's REAL_SEED_PATH. + A clone with its inbox moved aside now grades the clone (and fails on a + missing seed) instead of silently grading /root/... on the host.""" + src = _read(REFACTORED) + if HOST_SEED_LITERAL in src: + print("FAIL: refactored still hardcodes a host-absolute seed path") + return False + mod, exc, out, err = _safe_import() + if exc is not None or mod is None: + print("FAIL: import raised %r (stdout=%r stderr=%r)" % (exc, out, err)) + return False + resolved = os.path.realpath(mod.DEFAULT_PATH) + repo_root = os.path.realpath(os.path.dirname(os.path.dirname(WORK_DIR))) + if not (resolved == repo_root + or resolved.startswith(repo_root + os.sep)): + print("FAIL: DEFAULT_PATH %r is outside the tree root %r" + % (resolved, repo_root)) + return False + if os.path.realpath(REAL_SEED_PATH) != resolved: + print("FAIL: harness REAL_SEED_PATH (%r) != module DEFAULT_PATH (%r)" + % (os.path.realpath(REAL_SEED_PATH), resolved)) + return False + print("OK: seed path is tree-portable (resolves inside tree): %s" % resolved) + return True + + def main() -> None: checks = [ check_py_compile, @@ -379,6 +575,9 @@ def main() -> None: check_no_dead_imports, check_original_still_smells, check_path_override_honoured, + check_no_import_time_io, + check_m6_mutant_killed, + check_seed_path_portable, ] results = [fn() for fn in checks] passed = sum(results)