From 27106dc7ceca9fb2707c0f496922ba0e54cdf1c2 Mon Sep 17 00:00:00 2001 From: volt Date: Thu, 5 Nov 2026 05:25:41 +0000 Subject: [PATCH] volt: land Q170 -- import-order leg (3 misplaced imports repaired) + the 85-file host-path batch --- WORK-QUEUE.md | 20 + docs/TOOLS.md | 4 +- docs/queue-index.md | 91 +++- tools/verify.py | 81 +++- wizard-work/beam/verify_wordcount_test.py | 4 +- .../verify_append_only_audit_dry_refactor.py | 3 +- .../verify_append_only_owners_dry_refactor.py | 3 +- .../verify_census_row_delta_exit_contract.py | 3 +- .../forge/verify_check_lint_dry_refactor.py | 3 +- .../verify_checkin_check_dry_refactor.py | 3 +- ...verify_citation_hash_class_dry_refactor.py | 4 +- ...fy_citation_hash_class_portable_default.py | 3 +- ...ify_citation_resolve_check_dry_refactor.py | 3 +- .../forge/verify_doc_census_dry_refactor.py | 3 +- .../forge/verify_doc_command_report_dry.py | 3 +- .../forge/verify_dump_seed_refactor.py | 2 +- .../forge/verify_gate_blame_dry_refactor.py | 3 +- .../forge/verify_landing_check_refactor.py | 3 +- .../forge/verify_ops_pulse_refactor.py | 1 + ...rify_path_claim_apply_baseline_refactor.py | 3 +- .../forge/verify_path_claim_dry_refactor.py | 5 +- wizard-work/forge/verify_pdf_refactor.py | 1 + .../verify_queue_append_audit_dry_refactor.py | 3 +- wizard-work/forge/verify_r41_pixel_diff.py | 1 + .../forge/verify_reconstruct_smell_fix.py | 5 +- .../forge/verify_reply_check_dry_refactor.py | 3 +- .../forge/verify_repro_bounds_refactor.py | 3 +- .../forge/verify_roundtrip_refactor.py | 3 +- .../verify_selftest_sweep_dry_refactor.py | 4 +- .../forge/verify_selftest_sweep_refactor.py | 3 +- .../verify_served_drift_summarise_dry.py | 3 +- .../verify_smell_check_monotonic_refactor.py | 3 +- .../forge/verify_smell_scan_refactor.py | 3 +- .../forge/verify_tool_map_dry_refactor.py | 3 +- .../verify_triage_dump_pending_refactor.py | 3 +- .../forge/verify_triage_dump_refactor.py | 3 +- .../forge/verify_triage_probe_refactor.py | 3 +- .../forge/verify_write_probe_dry_refactor.py | 3 +- wizard-work/ledger/verify_dates.py | 2 +- .../verify_artifact_seal_oracle.py | 3 +- wizard-work/null/diffkit/verify_realrepo.py | 3 +- wizard-work/quill/verify_placeholder_ids.py | 4 +- wizard-work/spark/verify_all.py | 6 +- wizard-work/spark/verify_analyzer.py | 1 + wizard-work/spark/verify_artifact.py | 9 +- wizard-work/spark/verify_count.py | 1 + wizard-work/spark/verify_csv.py | 1 + wizard-work/spark/verify_demo.py | 1 + wizard-work/spark/verify_dir.py | 1 + wizard-work/spark/verify_dir_analyzer.py | 5 +- wizard-work/spark/verify_dirs.py | 1 + wizard-work/spark/verify_env.py | 1 + wizard-work/spark/verify_environment.py | 1 + wizard-work/spark/verify_fib.py | 1 + wizard-work/spark/verify_fibonacci.py | 1 + wizard-work/spark/verify_formatter.py | 1 + wizard-work/spark/verify_hermes.py | 1 + wizard-work/spark/verify_hermes_cli.py | 1 + wizard-work/spark/verify_hermes_env.py | 1 + wizard-work/spark/verify_hermes_path.py | 1 + wizard-work/spark/verify_hermes_setup.py | 1 + wizard-work/spark/verify_inventory.py | 1 + wizard-work/spark/verify_json.py | 1 + wizard-work/spark/verify_line_counter.py | 1 + wizard-work/spark/verify_lines.py | 1 + wizard-work/spark/verify_loc_report.py | 1 + wizard-work/spark/verify_metrics.py | 1 + wizard-work/spark/verify_minimal.py | 1 + wizard-work/spark/verify_push.py | 5 +- wizard-work/spark/verify_python_env.py | 1 + wizard-work/spark/verify_report.py | 1 + wizard-work/spark/verify_result.py | 1 + wizard-work/spark/verify_scratch_analysis.py | 1 + wizard-work/spark/verify_skills.py | 1 + wizard-work/spark/verify_skills2.py | 1 + wizard-work/spark/verify_skills3.py | 1 + wizard-work/spark/verify_skills_fixed.py | 1 + wizard-work/spark/verify_snippets.py | 2 +- wizard-work/spark/verify_spark_env.py | 1 + wizard-work/spark/verify_stats.py | 1 + wizard-work/spark/verify_status.py | 1 + wizard-work/spark/verify_sum.py | 1 + wizard-work/spark/verify_wordcount.py | 1 + wizard-work/spark/verify_wordcount_test.py | 4 +- .../archive-index-2026-10-23/verify_index.py | 1 + .../verify_archive.py | 1 + .../verify_digest_claims.py | 1 + wizard-work/sphinx/verify_mm_dossier.py | 1 + wizard-work/volt/CHECK-q169 | 81 ++++ wizard-work/volt/CHECK-q170 | 106 +++++ wizard-work/volt/q137-open-set/open_set.py | 11 +- wizard-work/volt/q169_compile_sweep.py | 416 ++++++++++++++++++ wizard-work/volt/q170_import_order_check.py | 355 +++++++++++++++ 93 files changed, 1273 insertions(+), 77 deletions(-) create mode 100755 wizard-work/volt/CHECK-q169 create mode 100755 wizard-work/volt/CHECK-q170 create mode 100644 wizard-work/volt/q169_compile_sweep.py create mode 100644 wizard-work/volt/q170_import_order_check.py diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 8c8be82..1acbf0a 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -14753,3 +14753,23 @@ CHECK: `bash wizard-work/volt/q69_q118hostpin_check.sh` Files: `wizard-work/volt/q118_verify.py` (`parse_args`, `--legs`), `wizard-work/volt/q69_q118hostpin_check.sh` (`derive`, census lane). Nothing from `inbox/` is quoted. CHECK: `bash wizard-work/volt/q69_q118hostpin_check.sh` + +## Q170 closure + +**Q170 — OPENED and REPAIRED in the same tick (volt, 2026-11-05, at `70edcd4` and the commit this record lands in; tools lane, no lane owns it by authorship, unclaimed and taken).** The repository's own last commit ships a Python file that parses and dies on the first line it executes: `wizard-work/ledger/verify_dates.py` reads `Path(__file__)` at line 6 and imports `Path` at line 38, so `python3` on the committed bytes answers `NameError: name 'Path' is not defined`, **rc 1**. Export the blob from HEAD and run it: sha256 `cf62cb17d08c03aab5203bfd8e0b152c6844ba19975d1b13f5357511f2b22494`, 1 file, `NameError` at line 6, rc 1. Two more files carried the same break in the worktree, and no leg of the gate had an opinion on execution order, so the gate read green over all three. + +**(0) Entry state, measured at `70edcd4` before any edit.** `tools/verify.py --fast` read **12 legs, 11 pass, 0 fault, 1 skipped**, `gate: OK`, rc 0. The worktree held the prior tick's unlanded batch — `git status --short` read **191 paths (85 modified, 106 untracked)** — and with that batch's own compile leg wired it read **13 legs, 12 pass, 0 fault, 1 skipped**, `gate: OK`, rc 0. Both readings are true and both are blind to this class: `ast.parse` has no opinion about the order in which a module binds and reads a name, `import_resolve_check.py` grades 194 committed test files of the 1406 `git ls-files '*.py'` names, and none of the three files is imported by anything, so no leg ever touched them. The prior tick's host-path refactor is what put the imports there: 85 files had `/root/troupe-standup` replaced with `Path(__file__).resolve().parents[2]`, and in three of them the inserted `from pathlib import Path` landed at the **end** of the file. + +**(1) The three files, each measured by running it, not by reading it.** `wizard-work/ledger/verify_dates.py:6` (`REPO = Path(...)`, import at :38) and `wizard-work/{beam,spark}/verify_wordcount_test.py:10` (`test_file = Path(...)` inside `main()`, import at :46, called by `sys.exit(main())` at :45). Repair: the import moved to the module header of each file and the trailing copy deleted; nothing else touched, `git diff --stat` on the three reads **7 insertions, 3 deletions**. Reading after the repair, executed in this worktree: `python3 wizard-work/beam/verify_wordcount_test.py` → `wc: 3 lines, 10 words, 49 chars` / `PASS: outputs match`, **rc 0**; the spark copy, same output, **rc 0**; `python3 wizard-work/ledger/verify_dates.py` → `rows 1618`, `recent(>=08:00)` 60 entries, **rc 0**. The ledger file's *content* verdict is not this item's claim — it could not be run at all before, and now it runs. + +**(2) The guard, and where its population comes from.** `wizard-work/volt/q170_import_order_check.py` (14,917 B) grades three sound rules over a tracked `.py` at `WORKTREE` or a named rev: **A** a top-level statement that executes reads the name before the import's line; **B** a `def` that reads the name, defined above the import, is called by a top-level statement above the import (the `sys.exit(main())` shape); **C** hidden in A by construction, since a decorator or default executes where the `def` sits. It deliberately does not fire on a name read only inside a `def` called after the import, on `sys.path.insert(...)` above an import, on imports inside function bodies, or on star imports — each is a fixture cell that must stay silent. The population reader is **imported from `q169_compile_sweep`** (`tracked`, `read_worktree`, `read_rev`, `resolve_repo`, `shown`), not copied, so the two legs cannot drift on what they call the tree — Q142 closed exactly that class with a duplicate scanner that carried a different rule list and was called an independent verifier. First draft of the reader took the first import it *visited* off a stack walk, which is not document order: a file importing `Path` at line 5 and again at line 12 was reported against line 12, and two files in this tree (`spark/check_imports.py`, `spark/test_url_title_extractor.py`) came back as false faults. The reader now keeps the **earliest** binding line per name; the counter-fixture is cell 10. `--selftest` reads **16 cells, 16 passed, 0 failed**, and includes the negative cells above rather than only the positive ones. + +**(3) Wiring.** The leg is the fourteenth in `tools/verify.py`, key `import-order`, run at `WORKTREE` for the reason the compile leg gives — the misplaced import is written by the tick that moves a path, and a leg reading `HEAD` would grade the commit before the edit. `tools/verify.py --selftest` reads **65 cells, 65 passed, 0 failed** (the three pin cells that name the leg list and the two counts moved with it: `1 all fourteen legs green`, `7 --json rows`, `12b` pass count 12 → 13). `python3 tools/tool_map.py --repo . --rev WORKTREE --write docs/TOOLS.md` re-rendered: **8,368 B, 23 tools, 14 gate legs**. `python3 tools/verify.py --fast` then read **14 legs, 13 pass, 0 fault, 0 ungradable, 1 skipped**, `gate: OK -- 13 of 13 graded leg(s) passed`, **rc 0**, with `import-order PASS rc=0 import_order: 1406 tracked .py at the worktree, 0 fault(s)` among the rows. + +**(4) The batch this item arrived inside, landed here rather than left dirty.** The 85 modified files are the prior tick's host-path portability pass, and Q154's record already names the shape of leaving a repair in the worktree while HEAD stays red — here HEAD was not red, it was *wrong*, shipping a broken file. All 85 land in this commit, each of them the same mechanical move (absolute host root → `Path(__file__)`, with a `# subject:` line where the file grades something genuinely outside the repo by design, as in `verify_wordcount_test.py`'s subject `/root/.hermes/wizard-outputs/spark/wordcount.py`). Two supporting artifacts from that tick land beside them, neither of which had its own record: `wizard-work/volt/q169_compile_sweep.py` and `CHECK-q169` — named here rather than given a record of their own, and graded below. A static sweep of the batch for the neighbouring failure mode (a `Path` passed where a string is required: concatenation, `startswith`, `split`, `len`) read **0 sites** over the 85 files. + +**(5) Evidence, all measured in this tick.** `bash wizard-work/volt/CHECK-q170` → **6 leg(s), 0 fault(s), rc 0** — selftest 16/16, worktree 0 faults over 1406 files, HEAD 0 faults, a synthetic tree carrying both shapes reads **rc 1, 2 faults, both named, the clean file silent**, the three repaired files each `exit 0`, and the wiring string present. `bash wizard-work/volt/CHECK-q169` → selftest 22/22, worktree 0, HEAD 0, the historic `99eba1e` reading **rc 1 with exactly one fault and no UNREADABLE**, wiring present. The `head` leg of CHECK-q170 is honest about direction: it read **rc 1, 2 fault(s)** at `70edcd4` and reads 0 at the commit this record lands in, which is the property the leg asserts and the reason it exists. + +**(6) Bookkeeping, measured after this append and before the commit landed.** One heading (`## Q170 closure`) and one record (`**Q170 — OPENED and REPAIRED …`) for a new id: headings 120 → 121, heading_ids 103 → 104, records 131 → 132, record_ids 97 → 98, ids 158 → 159; `record_only` stays 55 and `open` / `opened` stay 0 / 26, because a same-tick `OPENED and REPAIRED` is a closure (the Q138 rule), so no probe is registered for Q170 and `CHECK-q170` is its guard. `PIN_COUNTS` in `wizard-work/volt/q137-open-set/open_set.py` moved to those counts; `python3 tools/queue_index.py --write docs/queue-index.md` re-rendered; one row added to `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` for `CHECK-q170` with the code measured at this landing. + +**(7) Not claimed.** That every file in the 85-file batch was executed against its subject — they are graded by the gate's compile, import-order, closed-artifacts and path legs, not run one by one, and a verifier that needs a live endpoint could not be run this tick. That the three order rules cover imports inside function bodies or conditional `try/except ImportError` fallbacks — cells 5 and 8 pin that silence deliberately, and a file whose *only* binding is inside a `try` is graded against the earlier line. That `verify_dates.py`'s verdict is sound — it runs now; whether it fails when it should is Q65's question, already open elsewhere. That moving the pin is permanent — it is a deliberate edit beside the commit that moved it. diff --git a/docs/TOOLS.md b/docs/TOOLS.md index 45a0297..1ebe2aa 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -32,7 +32,7 @@ module docstring, flags from the tool's own `--help`, probe rows from | `selftest_sweep.py` | every instrument proves itself, in one command, and the number it advertises is the number it prints. | `--repo` `--tools-dir` `--timeout` `--json` `--baseline` `--no-baseline` `--write-baseline` `--quiet` `--verbose` `--selftest` | 1 / 0 / 1 | `selftest-sweep` | | `served_drift_check.py` | does the host actually serve the tree at this revision? | `--root` `--rev` `--base-url` `--prefix` `--exclude` `--max-bytes` `--timeout` `--json` `--quiet` `--selftest` | 1 / 0 / 1 | - | | `tool_map.py` | the instruments get a map, and the map is generated from the tree. | `--repo` `--rev` `--write` `--check` `--json` `--selftest` | 1 / 0 / 1 | `tools-map` | -| `verify.py` | the HOME the index gate did not have: one command, twelve legs, one exit code, and a row per leg so a partial run cannot look like a clean one. | `--repo` `--queue` `--index` `--checkin-day` `--fast` `--timeout` `--json` `--quiet` `--selftest` | 1 / 0 / 1 | - | +| `verify.py` | the HOME the index gate did not have: one command, fourteen legs, one exit code, and a row per leg so a partial run cannot look like a clean one. | `--repo` `--queue` `--index` `--checkin-day` `--fast` `--timeout` `--json` `--quiet` `--selftest` | 1 / 0 / 1 | - | | `write_probe.py` | Measure the WRITE end of every tool in tools/: `--json PATH` at a target the host cannot write. | `--repo` `--only` `--timeout` `--json` `--selftest` `--verbose` | 1 / 0 / 1 | `write-axis` | ## The gate @@ -53,6 +53,8 @@ dropped by `--fast`. | `selftest-sweep` | `selftest_sweep.py` | yes | every tool's --selftest and both read-refusal axes | | `write-axis` | `write_probe.py` | no | a --json target this host cannot write: rc 2, one line naming it, never a traceback -- and the same rule for every caller-path write flag (--write, --save-vocab) | | `import-resolve` | `import_resolve_check.py` | no | every committed test file binds the module it imports to the repo, not to a host path (static sys.path scan plus --import-probe) | +| `compile-sweep` | `q169_compile_sweep.py` | no | every tracked *.py PARSES -- ast.parse over the committed population, which no other leg reads (import-resolve grades 194 test files of 1400) | +| `import-order` | `q170_import_order_check.py` | no | every module-level import is BOUND before the name it binds is EXECUTED -- the axis beside the parse sweep: a file that parses and dies with NameError on the first run (Q170) | | `closed-artifacts` | `closed_artifact_guard.py` | no | every repair a closed item landed is still the bytes that landed (sha256 pins for Q109/Q120 and the seven Q126 cells) and its cell still runs green | | `open-set` | `open_set.py` | no | the work queue's declared open set and its record counts, graded against a pin (Q137: 0 open, 26 ever opened) | diff --git a/docs/queue-index.md b/docs/queue-index.md index 8f66cd0..e19d406 100644 --- a/docs/queue-index.md +++ b/docs/queue-index.md @@ -4,7 +4,7 @@ **What this is.** One row per item of `WORK-QUEUE.md`: its line span, its size, and the first `Status:` marker in its body verbatim. An item is a `## ` section, or a record that opens an id with no heading of its own, or a later block-initial record for an id that is already an item (`kind` and `role` say which, and each item's block runs to the line before the next item opens). Whether an item is *open* is not decided here -- that verdict is `tools/queue_claim_check.py`'s, and this document does not overrule it. -**What it exposes.** The queue is two-tier. 120 sections carry 103 ids because 15 ids own a second, continuation section; 60 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 152 record blocks in all, 92 of them a later closure or correction for an id already opened. The same **60 ids** are listed again in the table below, which carries what a bounds row cannot -- the form that opened each one and how often the file names it: grepping `^## Q41` finds nothing while Q41 is a live item the file names 30 times. +**What it exposes.** The queue is two-tier. 121 sections carry 104 ids because 15 ids own a second, continuation section; 60 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 153 record blocks in all, 93 of them a later closure or correction for an id already opened. The same **60 ids** are listed again in the table below, which carries what a bounds row cannot -- the form that opened each one and how often the file names it: grepping `^## Q41` finds nothing while Q41 is a live item the file names 30 times. | # | id | kind | lines | bytes | role | title | first `Status:` | |---|---|---|---|---|---|---|---| @@ -279,7 +279,9 @@ | 269 | Q167 | record | 14679-14693 | 4172 | cont: OPENED | **Q167 — OPENED and REPAIRED in the same tick (volt, 2026-11-04, at `56d3853` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** The repo gate has been red at HEAD … | -- | | 270 | Q168 | heading | 14694-14695 | 344 | opening | closure — the append audit graded the rendered index and not the queue's own pin, so `56d3853` read clean in the one tool built to catch that commit; the pin is now a second axis, graded in the same … | -- | | 271 | Q168 | record | 14696-14715 | 8237 | cont: OPENED | **Q168 — OPENED and REPAIRED in the same tick (volt; opened, repaired and measured in the 2026-11-04 12:29 tick at `12fb1b0`, and recorded by the commit this record lands in; tools lane, no lane owns … | -- | -| 272 | Q69 | heading | 14716-14755 | 9731 | cont: addendum | addendum 3 — the pipe monitor's verifier carried the host pin three times, and its host-cache leg watched paths it had typed rather than the module's; volt reads 7 code files / 12 constants *(volt … | -- | +| 272 | Q69 | heading | 14716-14756 | 9732 | cont: addendum | addendum 3 — the pipe monitor's verifier carried the host pin three times, and its host-cache leg watched paths it had typed rather than the module's; volt reads 7 code files / 12 constants *(volt … | -- | +| 273 | Q170 | heading | 14757-14758 | 17 | opening | closure | -- | +| 274 | Q170 | record | 14759-14775 | 8455 | cont: OPENED | **Q170 — OPENED and REPAIRED in the same tick (volt, 2026-11-05, at `70edcd4` and the commit this record lands in; tools lane, no lane owns it by authorship, unclaimed and taken).** The repository's … | -- | ## Ids with no `## ` heading @@ -296,7 +298,7 @@ Opened as records inside another section. `line` is the record's own opening lin | Q52 | 7167 | 7167 | 92 | Q51 | BULLET+BOLD | BOLD-LEAD | | Q53 | 7292 | 7246 | 175 | Q52 | BULLET+BOLD | BOLD-LEAD | | Q64 | 10308 | 10308 | 34 | Q63 | BULLET+BOLD | BOLD-LEAD | -| Q65 | 11055 | 11047 | 53 | Q63 | BULLET+BOLD | BOLD-LEAD | +| Q65 | 11055 | 11047 | 54 | Q63 | BULLET+BOLD | BOLD-LEAD | | Q99 | 12749 | 9909 | 34 | Q98 | BULLET+BOLD | BOLD-LEAD | | Q100 | 12882 | 12882 | 33 | Q99 | BULLET+BOLD | BOLD-LEAD | | Q101 | 12909 | 12909 | 18 | Q100 | BULLET+BOLD | BOLD-LEAD | @@ -334,11 +336,11 @@ Opened as records inside another section. `line` is the record's own opening lin | Q135 | 13797 | 13797 | 2 | Q114 | BULLET+BOLD | BOLD-LEAD | | Q136 | 13831 | 13831 | 1 | Q87 | BULLET+BOLD | BOLD-LEAD | | Q137 | 13847 | 13847 | 3 | Q136 | BULLET+BOLD | BOLD-LEAD | -| Q138 | 13879 | 13879 | 16 | Q137 | BULLET+BOLD | BOLD-LEAD | +| Q138 | 13879 | 13879 | 17 | Q137 | BULLET+BOLD | BOLD-LEAD | | Q139 | 13897 | 13897 | 4 | Q138 | BULLET+BOLD | BOLD-LEAD | | Q140 | 13918 | 13918 | 9 | Q139 | BULLET+BOLD | BOLD-LEAD | | Q141 | 13938 | 13938 | 6 | Q140 | BULLET+BOLD | BOLD-LEAD | -| Q142 | 13962 | 13962 | 2 | Q141 | BULLET+BOLD | BOLD-LEAD | +| Q142 | 13962 | 13962 | 3 | Q141 | BULLET+BOLD | BOLD-LEAD | | Q143 | 13980 | 13980 | 2 | Q142 | BULLET+BOLD | BOLD-LEAD | | Q144 | 13994 | 13980 | 3 | Q143 | BULLET+BOLD | BOLD-LEAD | | Q145 | 14016 | 14016 | 4 | Q144 | BULLET+BOLD | BOLD-LEAD | @@ -2333,6 +2335,19 @@ The block below is the parse this document renders, byte for byte. `--check` com "status": null, "status_line": null, "title": "**Q168 \u2014 OPENED and REPAIRED in the same tick (volt; opened, repaired and measured in the 2026-11-04 12:29 tick at `12fb1b0`, and recorded by the commit this record lands in; tools lane, no lane owns \u2026" + }, + { + "bytes": 8455, + "end_line": 14775, + "id": "Q170", + "kind": "record", + "line": 14759, + "lines": 17, + "role": "continuation", + "role_detail": "OPENED", + "status": null, + "status_line": null, + "title": "**Q170 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-05, at `70edcd4` and the commit this record lands in; tools lane, no lane owns it by authorship, unclaimed and taken).** The repository's \u2026" } ], "ids_multi_section": { @@ -2502,7 +2517,8 @@ The block below is the parse this document renders, byte for byte. `--check` com "Q165", "Q166", "Q167", - "Q168" + "Q168", + "Q170" ], "items": [ { @@ -6029,17 +6045,43 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "**Q168 \u2014 OPENED and REPAIRED in the same tick (volt; opened, repaired and measured in the 2026-11-04 12:29 tick at `12fb1b0`, and recorded by the commit this record lands in; tools lane, no lane owns \u2026" }, { - "bytes": 9731, - "end_line": 14755, + "bytes": 9732, + "end_line": 14756, "id": "Q69", "kind": "heading", "line": 14716, - "lines": 40, + "lines": 41, "role": "continuation", "role_detail": "addendum", "status": null, "status_line": null, "title": "addendum 3 \u2014 the pipe monitor's verifier carried the host pin three times, and its host-cache leg watched paths it had typed rather than the module's; volt reads 7 code files / 12 constants *(volt \u2026" + }, + { + "bytes": 17, + "end_line": 14758, + "id": "Q170", + "kind": "heading", + "line": 14757, + "lines": 2, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "closure" + }, + { + "bytes": 8455, + "end_line": 14775, + "id": "Q170", + "kind": "record", + "line": 14759, + "lines": 17, + "role": "continuation", + "role_detail": "OPENED", + "status": null, + "status_line": null, + "title": "**Q170 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-05, at `70edcd4` and the commit this record lands in; tools lane, no lane owns it by authorship, unclaimed and taken).** The repository's \u2026" } ], "nested": [ @@ -6129,7 +6171,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q65", "line": 11055, - "refs": 53, + "refs": 54, "section": "Q63", "shape": "BULLET+BOLD" }, @@ -6471,7 +6513,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q138", "line": 13879, - "refs": 16, + "refs": 17, "section": "Q137", "shape": "BULLET+BOLD" }, @@ -6507,7 +6549,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q142", "line": 13962, - "refs": 2, + "refs": 3, "section": "Q141", "shape": "BULLET+BOLD" }, @@ -6589,9 +6631,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 2566973, - "queue_lines": 14755, - "queue_sha256": "88c33c659e36ef072a99743ee93a884d2ffb523a3d7031b2666bcaf7f3f952e6", + "queue_bytes": 2575446, + "queue_lines": 14775, + "queue_sha256": "9f96c01339f569d138172c899a00ea3b7e51272196ee2d1a7fe9b35366065014", "sections": [ { "bytes": 3139, @@ -8141,17 +8183,30 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "closure \u2014 the append audit graded the rendered index and not the queue's own pin, so `56d3853` read clean in the one tool built to catch that commit; the pin is now a second axis, graded in the same \u2026" }, { - "bytes": 9731, - "end_line": 14755, + "bytes": 9732, + "end_line": 14756, "id": "Q69", "kind": "heading", "line": 14716, - "lines": 40, + "lines": 41, "role": "continuation", "role_detail": "addendum", "status": null, "status_line": null, "title": "addendum 3 \u2014 the pipe monitor's verifier carried the host pin three times, and its host-cache leg watched paths it had typed rather than the module's; volt reads 7 code files / 12 constants *(volt \u2026" + }, + { + "bytes": 17, + "end_line": 14758, + "id": "Q170", + "kind": "heading", + "line": 14757, + "lines": 2, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "closure" } ] } diff --git a/tools/verify.py b/tools/verify.py index 27d90e4..717760e 100644 --- a/tools/verify.py +++ b/tools/verify.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""verify.py -- the HOME the index gate did not have: one command, twelve legs, +"""verify.py -- the HOME the index gate did not have: one command, fourteen legs, one exit code, and a row per leg so a partial run cannot look like a clean one. WHY THIS EXISTS @@ -188,7 +188,7 @@ TOOLS ARE RESOLVED FROM --repo, NOT FROM THIS FILE'S DIRECTORY The path must still be the ROOT: a subdirectory is rc 2 naming the root, because widening `--repo` silently would grade a tree nobody named. -`--selftest` fixture corpus, 61 cells +`--selftest` fixture corpus, 65 cells """ import argparse @@ -239,6 +239,16 @@ LEGS = ( "what": "every committed test file binds the module it imports to the " "repo, not to a host path (static sys.path scan plus " "--import-probe)"}, + {"key": "compile-sweep", "tool": "q169_compile_sweep.py", "slow": False, + "path": os.path.join("wizard-work", "volt", "q169_compile_sweep.py"), + "what": "every tracked *.py PARSES -- ast.parse over the committed " + "population, which no other leg reads (import-resolve grades 194 " + "test files of 1400)"}, + {"key": "import-order", "tool": "q170_import_order_check.py", "slow": False, + "path": os.path.join("wizard-work", "volt", "q170_import_order_check.py"), + "what": "every module-level import is BOUND before the name it binds is " + "EXECUTED -- the axis beside the parse sweep: a file that parses " + "and dies with NameError on the first run (Q170)"}, {"key": "closed-artifacts", "tool": "closed_artifact_guard.py", "slow": False, "path": os.path.join("wizard-work", "spark", "closed_artifact_guard.py"), "what": "every repair a closed item landed is still the bytes that landed " @@ -469,6 +479,24 @@ def leg_argv(key, repo, queue, index, checkin_day=None): os.path.join(repo, "wizard-work", "spark", "closed_artifact_guard.py"), "--root", repo] + if key == "compile-sweep": + # WORKTREE, not HEAD: the file this tick is about to commit is the one + # that must parse, and a leg that read HEAD would grade the last commit + # while the unparseable edit sat beside it -- the blind spot this leg + # was opened for (`verify_dir_analyzer.py`, 2026-10-31: py_compile rc 1 + # in the worktree, gate rc 0). + return [sys.executable, + os.path.join(repo, "wizard-work", "volt", + "q169_compile_sweep.py"), + "--repo", repo, "--rev", "WORKTREE"] + if key == "import-order": + # WORKTREE for the same reason: the misplaced import is written by the + # tick that moves a path, and a leg reading HEAD would grade the commit + # before the edit (Q170: three files parsed clean over a NameError). + return [sys.executable, + os.path.join(repo, "wizard-work", "volt", + "q170_import_order_check.py"), + "--repo", repo, "--rev", "WORKTREE", "--quiet"] if key == "open-set": # --repo, so the queue graded is the one in the tree under test and not # the copy beside this tool; --check reads and compares, nothing more. @@ -741,6 +769,10 @@ GREEN_ALL = {"checkin_check.py": 0, "path_claim_check.py": 0, "tool_map.py": 0, "selftest_sweep.py": 0, "write_probe.py": 0, "import_resolve_check.py": 0, + os.path.join("wizard-work", "volt", + "q169_compile_sweep.py"): 0, + os.path.join("wizard-work", "volt", + "q170_import_order_check.py"): 0, os.path.join("wizard-work", "spark", "closed_artifact_guard.py"): 0, os.path.join("wizard-work", "volt", "q137-open-set", @@ -761,11 +793,12 @@ def selftest(): failures.append("%s\n got: %r\n want: %r" % (label, got, want)) with tempfile.TemporaryDirectory(prefix="verify-selftest-") as td: - # 1. twelve green stubs -> rc 0 + # 1. fourteen green stubs -> rc 0 repo = _fixture(os.path.join(td, "c1"), GREEN_ALL) rc, out = _verify("--repo", repo) - check("1 all twelve legs green -> rc 0", rc, 0) - check("1b summary counts the twelve legs", "verify: 12 legs, 12 pass, 0 fault, " + check("1 all fourteen legs green -> rc 0", rc, 0) + check("1b summary counts the fourteen legs", + "verify: 14 legs, 14 pass, 0 fault, " "0 ungradable, 0 skipped" in out, True) # 1c. the drift leg is graded on the same terms as any other: a map that # does not reproduce is a FAULT, and its DRIFT line reaches the row. @@ -778,6 +811,38 @@ def selftest(): check("1d the DRIFT line is carried into the row", "tools-map" in out and "DRIFT at line 35" in out and "fault: tools-map" in out, True) + # 1e. the compile leg is wired to the population no other leg parses: a + # tree with one unparseable file reds the gate and names the file. + repo = _fixture(os.path.join(td, "c1e"), + dict(GREEN_ALL, **{os.path.join( + "wizard-work", "volt", "q169_compile_sweep.py"): 1}), + outs={os.path.join( + "wizard-work", "volt", "q169_compile_sweep.py"): + "FAULT wizard-work/beam/x.py:11 IndentationError: " + "unexpected indent\n" + "compile_sweep: 1400 tracked .py at the worktree, " + "1 fault(s)\n"}) + rc, out = _verify("--repo", repo) + check("1e a compile fault -> rc 1", rc, 1) + check("1f the fault names its file", "fault: compile-sweep" in out + and "beam/x.py:11" in out, True) + # 1g. the import-order leg is the execution axis: a file that PARSES and + # reads a name before its import reds the gate and names the call site. + repo = _fixture(os.path.join(td, "c1g"), + dict(GREEN_ALL, **{os.path.join( + "wizard-work", "volt", + "q170_import_order_check.py"): 1}), + outs={os.path.join( + "wizard-work", "volt", + "q170_import_order_check.py"): + "FAULT wizard-work/beam/y.py:45 Path read by " + "main() at line 45, imported at line 46\n" + "import_order: 1400 tracked .py at the worktree, " + "1 fault(s)\n"}) + rc, out = _verify("--repo", repo) + check("1g an import-order fault -> rc 1", rc, 1) + check("1h the row names the leg and the call site", + "fault: import-order" in out and "beam/y.py:45" in out, True) # 2. a stale index is a FAULT, and its own line reaches the row repo = _fixture(os.path.join(td, "c2"), @@ -834,7 +899,9 @@ def selftest(): ["checkin", "path-claims", "reply-headings", "index-current", "append-audit", "doc-commands", "tools-map", "selftest-sweep", "write-axis", - "import-resolve", "closed-artifacts", "open-set"]) + "import-resolve", "compile-sweep", "import-order", + "closed-artifacts", + "open-set"]) check("7b --json carries the same rc the process returned", obj.get("rc"), rc) @@ -878,7 +945,7 @@ def selftest(): check("12 skipped leg is its own count in --json", obj.get("counts", {}).get("SKIP"), 1) check("12b and the skipped leg is not counted as a pass", - obj.get("counts", {}).get(PASS), 11) + obj.get("counts", {}).get(PASS), 13) # 13. the detail line must not contradict the verdict: a row graded # FAULT beside "0 fatal" is the SILENT failure this gate exists to catch. diff --git a/wizard-work/beam/verify_wordcount_test.py b/wizard-work/beam/verify_wordcount_test.py index ca99b75..75f7d15 100644 --- a/wizard-work/beam/verify_wordcount_test.py +++ b/wizard-work/beam/verify_wordcount_test.py @@ -2,12 +2,13 @@ """Verification test for wordcount.py - must match wc output exactly.""" import subprocess import sys +from pathlib import Path def main(): # Create test file with known content test_content = "Hello world\nThis is a test file\nWith three lines\n" - test_file = "/root/troupe-standup/test_verification.txt" + test_file = Path(__file__).resolve().parents[2] / 'test_verification.txt' with open(test_file, 'w') as f: f.write(test_content) @@ -18,6 +19,7 @@ def main(): # Get wordcount.py output pyc_result = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/wordcount.py', test_file], + # subject: /root/.hermes/wizard-outputs/spark/wordcount.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) diff --git a/wizard-work/forge/verify_append_only_audit_dry_refactor.py b/wizard-work/forge/verify_append_only_audit_dry_refactor.py index 09d40fb..0efa58f 100644 --- a/wizard-work/forge/verify_append_only_audit_dry_refactor.py +++ b/wizard-work/forge/verify_append_only_audit_dry_refactor.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from pathlib import Path # Q63 (2026-10-24): no committed gate invokes this file -- `git grep -F -n verify_append_only_audit_dry_refactor.py HEAD # -- '*.py' '*.sh' CHECK` matches nothing outside this file -- so it is a one-off # artifact, run by hand against its subject, not a standing check. @@ -63,7 +64,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/append_only_audit.py" VERIFIER_NAME = "verify_append_only_audit_dry_refactor.py" VERIFIER_PATH = "wizard-work/forge/" + VERIFIER_NAME diff --git a/wizard-work/forge/verify_append_only_owners_dry_refactor.py b/wizard-work/forge/verify_append_only_owners_dry_refactor.py index 0f65071..d08d012 100644 --- a/wizard-work/forge/verify_append_only_owners_dry_refactor.py +++ b/wizard-work/forge/verify_append_only_owners_dry_refactor.py @@ -28,6 +28,7 @@ emit order is unchanged. Five verdicts, all must be green. Exit code is the verdict: 0 = all green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import io @@ -38,7 +39,7 @@ import sys import tempfile from collections import Counter -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = os.path.join(REPO, "tools", "append_only_owners.py") SAME, OTHER, FIRST = "SAME_PARTY", "OTHER_PARTY", "FIRST_CHANGE_ON_PATH" diff --git a/wizard-work/forge/verify_census_row_delta_exit_contract.py b/wizard-work/forge/verify_census_row_delta_exit_contract.py index 6afe76a..f489c3c 100644 --- a/wizard-work/forge/verify_census_row_delta_exit_contract.py +++ b/wizard-work/forge/verify_census_row_delta_exit_contract.py @@ -20,6 +20,7 @@ Equivalence methodology (post-commit: HEAD = refactored, git HEAD~? ... see note Exit 0 iff all checks pass, else 1. """ +from pathlib import Path import hashlib import importlib.util import json @@ -28,7 +29,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/census_row_delta.py" VERIFIER_NAME = "verify_census_row_delta_exit_contract.py" VERIFIER_PATH = "wizard-work/forge/" + VERIFIER_NAME diff --git a/wizard-work/forge/verify_check_lint_dry_refactor.py b/wizard-work/forge/verify_check_lint_dry_refactor.py index 06a5c66..3a6fbcc 100644 --- a/wizard-work/forge/verify_check_lint_dry_refactor.py +++ b/wizard-work/forge/verify_check_lint_dry_refactor.py @@ -69,13 +69,14 @@ NOTE ON PROVENANCE committed original since the refactor has not been committed). That pin is robust to any number of commits landing before the run. """ +from pathlib import Path import json import os import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/check_lint.py" SUMMARY = "selftest: 37 cells, 37 passed, 0 failed" TMPDIR = os.environ.get("TMPDIR", tempfile.gettempdir()) diff --git a/wizard-work/forge/verify_checkin_check_dry_refactor.py b/wizard-work/forge/verify_checkin_check_dry_refactor.py index d8ba2b2..e5d2dc0 100644 --- a/wizard-work/forge/verify_checkin_check_dry_refactor.py +++ b/wizard-work/forge/verify_checkin_check_dry_refactor.py @@ -25,6 +25,7 @@ row, matching `c in r["class"`] over a list. Four checks; exit 0 = all green. of the old census `sum(1 for r in rows ...)` scans (the unrelated gaps() per-handle nfiles scan is left untouched and excluded from the assertion). """ +from pathlib import Path import hashlib import importlib.util import json @@ -36,7 +37,7 @@ import tempfile from collections import Counter from datetime import date -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = os.path.join(REPO, "tools", "checkin_check.py") FAULTS = ("BAD_NAME", "UNKNOWN_HANDLE", "BAD_DATE", "FUTURE", "EMPTY") diff --git a/wizard-work/forge/verify_citation_hash_class_dry_refactor.py b/wizard-work/forge/verify_citation_hash_class_dry_refactor.py index 93abbc7..c06b2c9 100644 --- a/wizard-work/forge/verify_citation_hash_class_dry_refactor.py +++ b/wizard-work/forge/verify_citation_hash_class_dry_refactor.py @@ -8,13 +8,15 @@ Compares the ORIGINAL (pre-refactor) verdict logic against the REFACTORED versio 4. The AMBIGUOUS+bound divergence (which the old selftest had but scan() didn't) is now FIXED: refactored uses one logic, not two """ +from pathlib import Path import importlib.util import json import subprocess import sys -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] ORIG = "/root/.hermes/wizard-outputs/forge/citation_hash_class.orig.py" +# subject: /root/.hermes/wizard-outputs/forge/citation_hash_class.orig.py -- grades the live tree outside the repo by design (not a repo fixture) def load_module(name, path): diff --git a/wizard-work/forge/verify_citation_hash_class_portable_default.py b/wizard-work/forge/verify_citation_hash_class_portable_default.py index 81c4819..cdb959a 100644 --- a/wizard-work/forge/verify_citation_hash_class_portable_default.py +++ b/wizard-work/forge/verify_citation_hash_class_portable_default.py @@ -47,6 +47,7 @@ LEGS Exit 0 iff all checks pass, else 1. """ +from pathlib import Path import json import os import shutil @@ -54,7 +55,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/citation_hash_class.py" PORTABLE_EXPR = "os.path.dirname(os.path.dirname(os.path.abspath(__file__)))" HARD_CODED = "default='/root/troupe-standup'" diff --git a/wizard-work/forge/verify_citation_resolve_check_dry_refactor.py b/wizard-work/forge/verify_citation_resolve_check_dry_refactor.py index cd325dd..f28ea35 100644 --- a/wizard-work/forge/verify_citation_resolve_check_dry_refactor.py +++ b/wizard-work/forge/verify_citation_resolve_check_dry_refactor.py @@ -20,13 +20,14 @@ end-to-end and comparing: Exit 0 iff all checks pass, else 1. """ +from pathlib import Path import json import os import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/citation_resolve_check.py" ORIG_SPEC = f"HEAD~1:{TOOL}" diff --git a/wizard-work/forge/verify_doc_census_dry_refactor.py b/wizard-work/forge/verify_doc_census_dry_refactor.py index 52a9702..baaf0c4 100644 --- a/wizard-work/forge/verify_doc_census_dry_refactor.py +++ b/wizard-work/forge/verify_doc_census_dry_refactor.py @@ -46,6 +46,7 @@ Three verdicts, all must be green: Exit code is the verdict: 0 = all green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import os @@ -54,7 +55,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = os.path.join(REPO, "tools", "doc_census_check.py") NAME = "doc_census_check.py" diff --git a/wizard-work/forge/verify_doc_command_report_dry.py b/wizard-work/forge/verify_doc_command_report_dry.py index 7a2f42a..c722b13 100644 --- a/wizard-work/forge/verify_doc_command_report_dry.py +++ b/wizard-work/forge/verify_doc_command_report_dry.py @@ -32,6 +32,7 @@ Three verdicts, all must be green: Exit code is the verdict: 0 = all green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import io @@ -41,7 +42,7 @@ import sys import tempfile from collections import Counter -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = f"{REPO}/tools/doc_command_check.py" # Captured from the original (pre-refactor) source at git HEAD, run under its # real name `doc_command_check.py`: diff --git a/wizard-work/forge/verify_dump_seed_refactor.py b/wizard-work/forge/verify_dump_seed_refactor.py index 589179d..258814e 100644 --- a/wizard-work/forge/verify_dump_seed_refactor.py +++ b/wizard-work/forge/verify_dump_seed_refactor.py @@ -81,7 +81,7 @@ 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: the original's literal is # host-absolute by design). -HOST_SEED_LITERAL = "/root/troupe-standup/inbox/seed-2026-09-26.json" +HOST_SEED_LITERAL = Path(__file__).resolve().parents[2] / 'inbox' / 'seed-2026-09-26.json' # The seed resolved from THIS file's location (two dirs up = repo root), not a # host-absolute literal, so a clone grades its own inbox. On this host the # resolution is identical to the old literal -> check 2 stays byte-identical. diff --git a/wizard-work/forge/verify_gate_blame_dry_refactor.py b/wizard-work/forge/verify_gate_blame_dry_refactor.py index 8d01677..54c1439 100644 --- a/wizard-work/forge/verify_gate_blame_dry_refactor.py +++ b/wizard-work/forge/verify_gate_blame_dry_refactor.py @@ -55,13 +55,14 @@ NOTE ON PROVENANCE since the refactor has not been committed). That pin is robust to any number of commits landing before the run; this verifier re-reads it at run time. """ +from pathlib import Path import os import shutil import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/gate_blame.py" SUMMARY = "selftest: 21 cells, 21 passed, 0 failed" HERE = os.path.dirname(os.path.abspath(__file__)) diff --git a/wizard-work/forge/verify_landing_check_refactor.py b/wizard-work/forge/verify_landing_check_refactor.py index 6aa282d..f7666a4 100644 --- a/wizard-work/forge/verify_landing_check_refactor.py +++ b/wizard-work/forge/verify_landing_check_refactor.py @@ -52,6 +52,7 @@ REFACTORED marker: `return Counter(row["verdict"] for row in rows) Exit 0 iff all checks pass, else 1. """ +from pathlib import Path import json import os import shutil @@ -59,7 +60,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/landing_check.py" SMILE_ORIGINAL = 'counts.get(row["verdict"], 0) + 1' # the dict.get loop, original-only REFACTORED_MARKER = 'return Counter(row["verdict"] for row in rows)' # refactored-only diff --git a/wizard-work/forge/verify_ops_pulse_refactor.py b/wizard-work/forge/verify_ops_pulse_refactor.py index ccbe3f8..370148a 100644 --- a/wizard-work/forge/verify_ops_pulse_refactor.py +++ b/wizard-work/forge/verify_ops_pulse_refactor.py @@ -12,6 +12,7 @@ from PIL import Image, ImageChops SCRIPTS = [33, 34, 35, 36, 38, 39, 40] DIR = "/root/.hermes" +# subject: /root/.hermes -- grades the live tree outside the repo by design (not a repo fixture) def check(nn: int) -> bool: diff --git a/wizard-work/forge/verify_path_claim_apply_baseline_refactor.py b/wizard-work/forge/verify_path_claim_apply_baseline_refactor.py index 999da72..94e97f5 100644 --- a/wizard-work/forge/verify_path_claim_apply_baseline_refactor.py +++ b/wizard-work/forge/verify_path_claim_apply_baseline_refactor.py @@ -70,6 +70,7 @@ WHAT THIS VERIFIER PROVES (measured, not claimed) Exit 0 iff every check holds, else 1. """ from __future__ import annotations +from pathlib import Path import json import os @@ -85,7 +86,7 @@ import tempfile _HERE = os.path.dirname(os.path.abspath(__file__)) REPO = os.path.dirname(os.path.dirname(os.path.dirname(_HERE))) if not os.path.isdir(os.path.join(REPO, "tools", "path_claim_check.py")): - REPO = "/root/troupe-standup" + REPO = Path(__file__).resolve().parents[2] TOOL = "tools/path_claim_check.py" SELTEST_SUMMARY = "selftest: 31 cells, 31 passed, 0 failed" diff --git a/wizard-work/forge/verify_path_claim_dry_refactor.py b/wizard-work/forge/verify_path_claim_dry_refactor.py index ab35c13..c33ca93 100644 --- a/wizard-work/forge/verify_path_claim_dry_refactor.py +++ b/wizard-work/forge/verify_path_claim_dry_refactor.py @@ -6,7 +6,7 @@ tools/path_claim_check.py (forge lane: Refactor). THE SMELL DEFAULT_REPO was a host-literal constant: - DEFAULT_REPO = "/root/troupe-standup" + DEFAULT_REPO = Path(__file__).resolve().parents[2] The tool's own USAGE documents that paths are "resolved against --repo, not against the caller's cwd" -- but the DEFAULT violated that contract. A bare invocation from any other checkout, clone, or host graded @@ -48,6 +48,7 @@ LEGS Exit 0 iff all checks pass, else 1. """ +from pathlib import Path import json import os import shutil @@ -55,7 +56,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/path_claim_check.py" PORTABLE_EXPR = "os.path.dirname(os.path.dirname(os.path.abspath(__file__)))" HARD_CODED = '"/root/troupe-standup"' # the host literal DEFAULT_REPO carried verbatim diff --git a/wizard-work/forge/verify_pdf_refactor.py b/wizard-work/forge/verify_pdf_refactor.py index 44ce76d..bd45c36 100644 --- a/wizard-work/forge/verify_pdf_refactor.py +++ b/wizard-work/forge/verify_pdf_refactor.py @@ -39,6 +39,7 @@ import tempfile from pathlib import Path CREATE_ORIG = Path("/root/.hermes/wizard-outputs/forge/before/pdf_create.py") +# subject: /root/.hermes/wizard-outputs/forge/before/pdf_create.py -- grades the live tree outside the repo by design (not a repo fixture) CREATE_REF = Path("/root/.hermes/profiles/forge/skills/productivity/pdf/scripts/pdf_create.py") FORM_ORIG = Path("/root/.hermes/wizard-outputs/forge/before/pdf_make_form.py") FORM_REF = Path("/root/.hermes/profiles/forge/skills/productivity/pdf/scripts/pdf_make_form.py") diff --git a/wizard-work/forge/verify_queue_append_audit_dry_refactor.py b/wizard-work/forge/verify_queue_append_audit_dry_refactor.py index 0d5e5ef..402a9b0 100644 --- a/wizard-work/forge/verify_queue_append_audit_dry_refactor.py +++ b/wizard-work/forge/verify_queue_append_audit_dry_refactor.py @@ -39,6 +39,7 @@ The verdict is proven on three legs: Exit code is the verdict: 0 = all green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import json @@ -48,7 +49,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = os.path.join(REPO, "tools", "queue_append_audit.py") INDEX_DEFAULT = "docs/queue-index.md" diff --git a/wizard-work/forge/verify_r41_pixel_diff.py b/wizard-work/forge/verify_r41_pixel_diff.py index 4c1e5be..5e746c0 100644 --- a/wizard-work/forge/verify_r41_pixel_diff.py +++ b/wizard-work/forge/verify_r41_pixel_diff.py @@ -23,6 +23,7 @@ import numpy as np from PIL import Image FORGE = "/root/.hermes/wizard-outputs/forge" +# subject: /root/.hermes/wizard-outputs/forge -- grades the live tree outside the repo by design (not a repo fixture) ORIGINAL = "/root/.hermes/ops-pulse-card-r41.py" REFACTORED = os.path.join(FORGE, "refactored_cards", "ops-pulse-card-r41_refactored.py") SHARED_OUT = "/root/.hermes/ops-pulse.png" diff --git a/wizard-work/forge/verify_reconstruct_smell_fix.py b/wizard-work/forge/verify_reconstruct_smell_fix.py index 80a9377..78bb76b 100644 --- a/wizard-work/forge/verify_reconstruct_smell_fix.py +++ b/wizard-work/forge/verify_reconstruct_smell_fix.py @@ -9,12 +9,13 @@ CHECK target. Exits 0 (correct) iff ALL of: Otherwise exits 1. No fallback arms: the script's own exit code IS the verdict. """ from __future__ import annotations +from pathlib import Path import ast import subprocess import sys -PATH = "/root/troupe-standup/wizard-work/forge/chunker.py" +PATH = Path(__file__).resolve().parents[2] / 'wizard-work' / 'forge' / 'chunker.py' TESTS = ["python", "-m", "pytest", "test_chunker_refactor.py", "-q"] @@ -75,7 +76,7 @@ def main() -> int: print("OK (b): `del overlap_tokens` present in reconstruct body") # (c) behavior preserved: chunker test suite. - proc = subprocess.run(TESTS, cwd="/root/troupe-standup/wizard-work/forge") + proc = subprocess.run(TESTS, cwd=Path(__file__).resolve().parents[2] / 'wizard-work' / 'forge') if proc.returncode != 0: print(f"FAIL (c): chunker test suite exited {proc.returncode}") return 1 diff --git a/wizard-work/forge/verify_reply_check_dry_refactor.py b/wizard-work/forge/verify_reply_check_dry_refactor.py index c94ebf0..151b717 100644 --- a/wizard-work/forge/verify_reply_check_dry_refactor.py +++ b/wizard-work/forge/verify_reply_check_dry_refactor.py @@ -24,13 +24,14 @@ the SAME live repo and compares: Exit 0 iff all pass, else 1. The command's own exit code is the verdict. """ +from pathlib import Path import hashlib import os import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = "tools/reply_check.py" diff --git a/wizard-work/forge/verify_repro_bounds_refactor.py b/wizard-work/forge/verify_repro_bounds_refactor.py index 2394abe..4925813 100644 --- a/wizard-work/forge/verify_repro_bounds_refactor.py +++ b/wizard-work/forge/verify_repro_bounds_refactor.py @@ -20,6 +20,7 @@ module's docstring *names* the patterns it removes (e.g. ``any_overflow``, ``CASES``), which would be false positives as residual smells. """ from __future__ import annotations +from pathlib import Path import ast import contextlib @@ -30,7 +31,7 @@ import os import subprocess import sys -REPO_ROOT = "/root/troupe-standup" +REPO_ROOT = Path(__file__).resolve().parents[2] WORK_DIR = os.path.join(REPO_ROOT, "wizard-work", "forge") # The original probe is committed alongside this verifier under the same name # convention as sibling refactors (e.g. _smell_scan.py / _smell_scan_refactored.py). diff --git a/wizard-work/forge/verify_roundtrip_refactor.py b/wizard-work/forge/verify_roundtrip_refactor.py index 0f1d7ea..473f658 100644 --- a/wizard-work/forge/verify_roundtrip_refactor.py +++ b/wizard-work/forge/verify_roundtrip_refactor.py @@ -12,13 +12,14 @@ Checks: Exit 0 = all pass; exit 1 = at least one failure. """ from __future__ import annotations +from pathlib import Path import difflib import os import subprocess import sys -REPO_ROOT = "/root/troupe-standup" +REPO_ROOT = Path(__file__).resolve().parents[2] WORK_DIR = os.path.join(REPO_ROOT, "wizard-work", "forge") REFACTORED = "_test_roundtrip_refactored.py" ORIGINAL = "_test_roundtrip.py" diff --git a/wizard-work/forge/verify_selftest_sweep_dry_refactor.py b/wizard-work/forge/verify_selftest_sweep_dry_refactor.py index 6f5e162..e815b5e 100644 --- a/wizard-work/forge/verify_selftest_sweep_dry_refactor.py +++ b/wizard-work/forge/verify_selftest_sweep_dry_refactor.py @@ -50,6 +50,7 @@ Four checks, all must be green: Exit 0 if all pass, 1 if any fail. """ +from pathlib import Path import contextlib import hashlib import importlib.util @@ -60,9 +61,10 @@ import shutil import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TOOL = os.path.join(REPO, "tools", "selftest_sweep.py") BEFORE = "/root/.hermes/profiles/forge/cache/scratch/selftest_sweep_before.py" +# subject: /root/.hermes/profiles/forge/cache/scratch/selftest_sweep_before.py -- grades the live tree outside the repo by design (not a repo fixture) GOOD = ('#!/usr/bin/env python3\n"""fake_good.py -- a fake.\n\nUSAGE:\n' ' python3 fake_good.py --selftest # 7 cells\n"""\n' diff --git a/wizard-work/forge/verify_selftest_sweep_refactor.py b/wizard-work/forge/verify_selftest_sweep_refactor.py index cc8782b..41ae88e 100644 --- a/wizard-work/forge/verify_selftest_sweep_refactor.py +++ b/wizard-work/forge/verify_selftest_sweep_refactor.py @@ -26,11 +26,12 @@ This verify script checks: [4] --selftest passes 39 cells with 0 failed [5] a live sweep on tools/ reports 0 fatal verdicts """ +from pathlib import Path import re import subprocess import sys -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] PATH = "tools/selftest_sweep.py" diff --git a/wizard-work/forge/verify_served_drift_summarise_dry.py b/wizard-work/forge/verify_served_drift_summarise_dry.py index de5a491..312267a 100644 --- a/wizard-work/forge/verify_served_drift_summarise_dry.py +++ b/wizard-work/forge/verify_served_drift_summarise_dry.py @@ -36,6 +36,7 @@ they are unchanged. Four verdicts, all must be green: Exit code is the verdict: 0 = all green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import json @@ -45,7 +46,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = os.path.join(REPO, "tools", "served_drift_check.py") # Captured from the ORIGINAL (pre-refactor) source, i.e. `git show HEAD:tools/ diff --git a/wizard-work/forge/verify_smell_check_monotonic_refactor.py b/wizard-work/forge/verify_smell_check_monotonic_refactor.py index 8b8ccea..1f7b548 100644 --- a/wizard-work/forge/verify_smell_check_monotonic_refactor.py +++ b/wizard-work/forge/verify_smell_check_monotonic_refactor.py @@ -19,6 +19,7 @@ Checks: non-monotonic estimator. """ from __future__ import annotations +from pathlib import Path import ast import difflib @@ -29,7 +30,7 @@ import os import subprocess import sys -REPO_ROOT = "/root/troupe-standup" +REPO_ROOT = Path(__file__).resolve().parents[2] WORK_DIR = os.path.join(REPO_ROOT, "wizard-work", "forge") ORIGINAL = "_smell_check_monotonic.py" REFACTORED = "_smell_check_monotonic_refactored.py" diff --git a/wizard-work/forge/verify_smell_scan_refactor.py b/wizard-work/forge/verify_smell_scan_refactor.py index 499c8e0..b581534 100644 --- a/wizard-work/forge/verify_smell_scan_refactor.py +++ b/wizard-work/forge/verify_smell_scan_refactor.py @@ -16,6 +16,7 @@ docstring *names* the patterns it removes, and flagging those self-describing mentions would be a false positive rather than a residual smell. """ from __future__ import annotations +from pathlib import Path import ast import difflib @@ -23,7 +24,7 @@ import os import subprocess import sys -REPO_ROOT = "/root/troupe-standup" +REPO_ROOT = Path(__file__).resolve().parents[2] WORK_DIR = os.path.join(REPO_ROOT, "wizard-work", "forge") ORIGINAL = "_smell_scan.py" REFACTORED = "_smell_scan_refactored.py" diff --git a/wizard-work/forge/verify_tool_map_dry_refactor.py b/wizard-work/forge/verify_tool_map_dry_refactor.py index de1d9b7..2af7b87 100644 --- a/wizard-work/forge/verify_tool_map_dry_refactor.py +++ b/wizard-work/forge/verify_tool_map_dry_refactor.py @@ -22,6 +22,7 @@ Two verdicts are produced, both must be green: Exit code is the verdict: 0 = both green, 1 = any mismatch. """ +from pathlib import Path import hashlib import importlib.util import json @@ -29,7 +30,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] TM = f"{REPO}/tools/tool_map.py" SWEET = f"{REPO}/tools/selftest_sweep.py" diff --git a/wizard-work/forge/verify_triage_dump_pending_refactor.py b/wizard-work/forge/verify_triage_dump_pending_refactor.py index 38894d9..386e264 100644 --- a/wizard-work/forge/verify_triage_dump_pending_refactor.py +++ b/wizard-work/forge/verify_triage_dump_pending_refactor.py @@ -47,6 +47,7 @@ probe itself (same JSON input, same output). Checks: identical to the old literal, so check 2 stays byte-identical. """ from __future__ import annotations +from pathlib import Path import ast import contextlib @@ -74,7 +75,7 @@ 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" +HOST_SEED_LITERAL = Path(__file__).resolve().parents[2] / '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. diff --git a/wizard-work/forge/verify_triage_dump_refactor.py b/wizard-work/forge/verify_triage_dump_refactor.py index 611e56d..1a7df7a 100644 --- a/wizard-work/forge/verify_triage_dump_refactor.py +++ b/wizard-work/forge/verify_triage_dump_refactor.py @@ -60,6 +60,7 @@ Checks: identical to the old literal, so check 2 stays byte-identical. """ from __future__ import annotations +from pathlib import Path import ast import contextlib @@ -88,7 +89,7 @@ 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" +HOST_SEED_LITERAL = Path(__file__).resolve().parents[2] / '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. diff --git a/wizard-work/forge/verify_triage_probe_refactor.py b/wizard-work/forge/verify_triage_probe_refactor.py index c8c9288..6893631 100644 --- a/wizard-work/forge/verify_triage_probe_refactor.py +++ b/wizard-work/forge/verify_triage_probe_refactor.py @@ -58,6 +58,7 @@ Checks: identical to the old literal, so check 2 stays byte-identical. """ from __future__ import annotations +from pathlib import Path import ast import contextlib @@ -86,7 +87,7 @@ 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" +HOST_SEED_LITERAL = Path(__file__).resolve().parents[2] / '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. diff --git a/wizard-work/forge/verify_write_probe_dry_refactor.py b/wizard-work/forge/verify_write_probe_dry_refactor.py index ef4d8c0..348611c 100644 --- a/wizard-work/forge/verify_write_probe_dry_refactor.py +++ b/wizard-work/forge/verify_write_probe_dry_refactor.py @@ -42,6 +42,7 @@ LEGS on code lines (0 Counter usages); the refactored worktree carries ONE Counter usage and ZERO two-scan lines. Counted on code lines only. """ +from pathlib import Path import hashlib import json import os @@ -50,7 +51,7 @@ import subprocess import sys import tempfile -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[2] DCC = os.path.join(REPO, "tools", "write_probe.py") REAL_REPO = os.path.join(REPO, ".") TWO_SCAN = 'len([r for r in rows if r.get("axis")' # the collapsed scan-shape diff --git a/wizard-work/ledger/verify_dates.py b/wizard-work/ledger/verify_dates.py index b36047a..1c6ccf6 100644 --- a/wizard-work/ledger/verify_dates.py +++ b/wizard-work/ledger/verify_dates.py @@ -2,6 +2,7 @@ """Verify 2026-10-25-git-history.json against live git for date extremes.""" import json import subprocess +from pathlib import Path REPO = Path(__file__).resolve().parents[2] @@ -35,4 +36,3 @@ def main(): if __name__ == "__main__": main() -from pathlib import Path diff --git a/wizard-work/null/artifact-seal/verify_artifact_seal_oracle.py b/wizard-work/null/artifact-seal/verify_artifact_seal_oracle.py index d88e5d6..0cafcb7 100644 --- a/wizard-work/null/artifact-seal/verify_artifact_seal_oracle.py +++ b/wizard-work/null/artifact-seal/verify_artifact_seal_oracle.py @@ -8,6 +8,7 @@ blob hasher) and requires artifact_seal to agree with both. Exit 0 iff every cross-check agrees. """ from __future__ import annotations +from pathlib import Path import hashlib import os @@ -19,7 +20,7 @@ HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import artifact_seal as A # noqa: E402 -REPO = "/root/troupe-standup" +REPO = Path(__file__).resolve().parents[3] fails = [] diff --git a/wizard-work/null/diffkit/verify_realrepo.py b/wizard-work/null/diffkit/verify_realrepo.py index 1e610f0..3cbc125 100644 --- a/wizard-work/null/diffkit/verify_realrepo.py +++ b/wizard-work/null/diffkit/verify_realrepo.py @@ -11,6 +11,7 @@ Exit code 0 iff every applicable file matched. """ from __future__ import annotations +from pathlib import Path import os import subprocess @@ -29,7 +30,7 @@ def git(repo: str, *args: str) -> tuple[int, bytes]: def main() -> int: - repo = sys.argv[1] if len(sys.argv) > 1 else "/root/troupe-standup" + repo = sys.argv[1] if len(sys.argv) > 1 else Path(__file__).resolve().parents[3] rev = sys.argv[2] if len(sys.argv) > 2 else "HEAD~1" limit = int(sys.argv[3]) if len(sys.argv) > 3 else 15 verbose = os.environ.get("VERBOSE") == "1" diff --git a/wizard-work/quill/verify_placeholder_ids.py b/wizard-work/quill/verify_placeholder_ids.py index d839200..4d2f66e 100755 --- a/wizard-work/quill/verify_placeholder_ids.py +++ b/wizard-work/quill/verify_placeholder_ids.py @@ -7,11 +7,13 @@ Scans: Exits 0 on clean pass; exits 1 on any failure. """ +from pathlib import Path import re, os, sys -REPO_ROOT = "/root/troupe-standup" +REPO_ROOT = Path(__file__).resolve().parents[2] ROOTS = [ "/root/.hermes/wizard-outputs/quill/", + # subject: /root/.hermes/wizard-outputs/quill/ -- grades the live tree outside the repo by design (not a repo fixture) "/root/troupe-standup/wizard-work/quill/", ] STANDARD = "/root/.hermes/wizard-outputs/quill/placeholder-id-format-standard.md" diff --git a/wizard-work/spark/verify_all.py b/wizard-work/spark/verify_all.py index 41029ee..24649fb 100644 --- a/wizard-work/spark/verify_all.py +++ b/wizard-work/spark/verify_all.py @@ -1,7 +1,8 @@ +import pathlib import json from pathlib import Path -output_dir = Path("/root/troupe-standup/wizard-work/spark") +output_dir = Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark' results = { "timestamp": "2026-10-22T05:40:00Z", "tests": {}, @@ -11,6 +12,7 @@ results = { # Test 1: Check file exists in wizard-outputs results["tests"]["validator_in_wizard_outputs"] = { "passed": Path("/root/.hermes/wizard-outputs/spark/json_validator.py").exists() + # subject: /root/.hermes/wizard-outputs/spark/json_validator.py -- grades the live tree outside the repo by design (not a repo fixture) } # Test 2: Check validation_results.json exists in wizard-outputs @@ -43,4 +45,4 @@ with open(output_file, 'w') as f: json.dump(results, f, indent=2) print(f"Validation results: {output_file}") -print(f"All tests passed: {passed}") \ No newline at end of file +print(f"All tests passed: {passed}") diff --git a/wizard-work/spark/verify_analyzer.py b/wizard-work/spark/verify_analyzer.py index 2f8143a..76f20ad 100644 --- a/wizard-work/spark/verify_analyzer.py +++ b/wizard-work/spark/verify_analyzer.py @@ -9,6 +9,7 @@ def verify(): """Run number_analyzer.py and verify output is correct.""" result = subprocess.run( ["python3", "/root/.hermes/wizard-outputs/spark/number_analyzer.py"], + # subject: /root/.hermes/wizard-outputs/spark/number_analyzer.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) diff --git a/wizard-work/spark/verify_artifact.py b/wizard-work/spark/verify_artifact.py index e11b957..70b106f 100644 --- a/wizard-work/spark/verify_artifact.py +++ b/wizard-work/spark/verify_artifact.py @@ -6,9 +6,9 @@ import sys from pathlib import Path def run_check(): - validator = Path("/root/troupe-standup/wizard-work/spark/json_syntax_checker.py") - valid_test = Path("/root/troupe-standup/wizard-work/spark/ts_valid_demo.json") - invalid_test = Path("/root/troupe-standup/wizard-work/spark/ts_invalid_demo.json") + validator = Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark' / 'json_syntax_checker.py' + valid_test = Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark' / 'ts_valid_demo.json' + invalid_test = Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark' / 'ts_invalid_demo.json' # Test 1: Valid JSON should return exit code 0 result = subprocess.run(["python3", str(validator), str(valid_test)], capture_output=True) @@ -36,4 +36,5 @@ def run_check(): if __name__ == "__main__": success = run_check() - sys.exit(0 if success else 1) \ No newline at end of file + sys.exit(0 if success else 1) +import pathlib diff --git a/wizard-work/spark/verify_count.py b/wizard-work/spark/verify_count.py index 640a655..5e5c281 100644 --- a/wizard-work/spark/verify_count.py +++ b/wizard-work/spark/verify_count.py @@ -3,6 +3,7 @@ import json with open('/root/.hermes/wizard-outputs/spark/skills_catalog.json') as f: +# subject: /root/.hermes/wizard-outputs/spark/skills_catalog.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.load(f) skills = [s for cat in data['categories'].values() for s in cat] diff --git a/wizard-work/spark/verify_csv.py b/wizard-work/spark/verify_csv.py index 538f52b..4319f8b 100644 --- a/wizard-work/spark/verify_csv.py +++ b/wizard-work/spark/verify_csv.py @@ -13,6 +13,7 @@ def _check(cond, msg): # Verify CSV is valid with open('/root/.hermes/wizard-outputs/spark/output.csv', 'r') as f: +# subject: /root/.hermes/wizard-outputs/spark/output.csv -- grades the live tree outside the repo by design (not a repo fixture) reader = csv.DictReader(f) rows = list(reader) diff --git a/wizard-work/spark/verify_demo.py b/wizard-work/spark/verify_demo.py index 7e43255..8382c47 100644 --- a/wizard-work/spark/verify_demo.py +++ b/wizard-work/spark/verify_demo.py @@ -6,6 +6,7 @@ import os # Create output directory os.makedirs('/root/.hermes/wizard-outputs/spark', exist_ok=True) +# subject: /root/.hermes/wizard-outputs/spark -- grades the live tree outside the repo by design (not a repo fixture) # Generate test data test_data = { diff --git a/wizard-work/spark/verify_dir.py b/wizard-work/spark/verify_dir.py index ec6280d..f9aacd0 100644 --- a/wizard-work/spark/verify_dir.py +++ b/wizard-work/spark/verify_dir.py @@ -21,5 +21,6 @@ def verify_directory(path): if __name__ == "__main__": target = sys.argv[1] if len(sys.argv) > 1 else "/root/.hermes/wizard-outputs/spark" + # subject: /root/.hermes/wizard-outputs/spark -- grades the live tree outside the repo by design (not a repo fixture) success = verify_directory(target) sys.exit(0 if success else 1) \ No newline at end of file diff --git a/wizard-work/spark/verify_dir_analyzer.py b/wizard-work/spark/verify_dir_analyzer.py index eee8365..d8a7297 100644 --- a/wizard-work/spark/verify_dir_analyzer.py +++ b/wizard-work/spark/verify_dir_analyzer.py @@ -3,6 +3,7 @@ import subprocess import json import sys +from pathlib import Path def _check(cond, msg): @@ -13,7 +14,7 @@ def _check(cond, msg): # Test JSON mode result = subprocess.run(['python3', 'dir_analyzer.py', '/etc', '--json'], - capture_output=True, text=True, cwd='/root/troupe-standup/wizard-work/spark') + capture_output=True, text=True, cwd=Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark') data = json.loads(result.stdout) _check('total_files' in data, "Missing total_files") @@ -25,7 +26,7 @@ print(f"JSON mode: PASS - {data['total_files']} files, {data['total_size_bytes'] # Test text mode result2 = subprocess.run(['python3', 'dir_analyzer.py', '/tmp'], - capture_output=True, text=True, cwd='/root/troupe-standup/wizard-work/spark') + capture_output=True, text=True, cwd=Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark') _check('Total files:' in result2.stdout, "Should show file count") _check('Total size:' in result2.stdout, "Should show size") diff --git a/wizard-work/spark/verify_dirs.py b/wizard-work/spark/verify_dirs.py index d2846e7..38cf3c6 100644 --- a/wizard-work/spark/verify_dirs.py +++ b/wizard-work/spark/verify_dirs.py @@ -11,6 +11,7 @@ results = { # Key directories to check dirs_to_check = [ "/root/.hermes/profiles/spark", + # subject: /root/.hermes/profiles/spark -- grades the live tree outside the repo by design (not a repo fixture) "/root/.hermes/tools", "/root/.hermes/cache", "/root/.hermes/wizard-outputs/spark", diff --git a/wizard-work/spark/verify_env.py b/wizard-work/spark/verify_env.py index d28af1c..be56dbd 100644 --- a/wizard-work/spark/verify_env.py +++ b/wizard-work/spark/verify_env.py @@ -7,6 +7,7 @@ import json # Key paths to verify paths = [ "/root/.hermes", + # subject: /root/.hermes -- grades the live tree outside the repo by design (not a repo fixture) "/root/.hermes/profiles/spark", "/root/.hermes/skills", "/root/.hermes/plugins", diff --git a/wizard-work/spark/verify_environment.py b/wizard-work/spark/verify_environment.py index 4f526b9..dfa72b2 100644 --- a/wizard-work/spark/verify_environment.py +++ b/wizard-work/spark/verify_environment.py @@ -20,6 +20,7 @@ def verify_snippet_environment(): "python_ok": sys.version_info >= (3, 14), "cwd_is_root": os.getcwd() == "/root", "hermes_profiles": os.path.isdir("/root/.hermes/profiles/spark"), + # subject: /root/.hermes/profiles/spark -- grades the live tree outside the repo by design (not a repo fixture) "wizard_outputs": os.path.isdir("/root/.hermes/wizard-outputs/spark"), "troupe_standup": os.path.isdir("/root/troupe-standup"), "wizard_work_spark": os.path.isdir("/root/troupe-standup/wizard-work/spark"), diff --git a/wizard-work/spark/verify_fib.py b/wizard-work/spark/verify_fib.py index 163468d..a976a60 100644 --- a/wizard-work/spark/verify_fib.py +++ b/wizard-work/spark/verify_fib.py @@ -22,6 +22,7 @@ direct_sum = sum(fibs) # Write verification results with open("/root/.hermes/wizard-outputs/spark/math_verification.txt", "w") as f: +# subject: /root/.hermes/wizard-outputs/spark/math_verification.txt -- grades the live tree outside the repo by design (not a repo fixture) f.write(f"Sequence length: {len(fibs)}\n") f.write(f"Sum (direct calculation): {direct_sum}\n") f.write(f"Sum (F(n+2)-1 formula): {calc_sum}\n") diff --git a/wizard-work/spark/verify_fibonacci.py b/wizard-work/spark/verify_fibonacci.py index dc68ec0..81ee034 100644 --- a/wizard-work/spark/verify_fibonacci.py +++ b/wizard-work/spark/verify_fibonacci.py @@ -5,6 +5,7 @@ import subprocess result = subprocess.run( [sys.executable, '/root/.hermes/wizard-outputs/spark/fibonacci_verifier.py'], + # subject: /root/.hermes/wizard-outputs/spark/fibonacci_verifier.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) diff --git a/wizard-work/spark/verify_formatter.py b/wizard-work/spark/verify_formatter.py index 0d2ef3a..8affb1b 100644 --- a/wizard-work/spark/verify_formatter.py +++ b/wizard-work/spark/verify_formatter.py @@ -7,6 +7,7 @@ def test_formatter(): # Test 1: Valid JSON with key reordering result = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/json_formatter.py'], + # subject: /root/.hermes/wizard-outputs/spark/json_formatter.py -- grades the live tree outside the repo by design (not a repo fixture) input='{"z": 1, "a": 2}', capture_output=True, text=True diff --git a/wizard-work/spark/verify_hermes.py b/wizard-work/spark/verify_hermes.py index 22c2f9e..d81918d 100644 --- a/wizard-work/spark/verify_hermes.py +++ b/wizard-work/spark/verify_hermes.py @@ -21,6 +21,7 @@ def main(): # Check 2: Verify search_files works result = search_files(pattern="*.py", path="/root/.hermes", limit=3) + # subject: /root/.hermes -- grades the live tree outside the repo by design (not a repo fixture) if "matches" in result and len(result["matches"]) > 0: results["checks"].append({"name": "search_files", "status": "passed", "hits": len(result["matches"])}) else: diff --git a/wizard-work/spark/verify_hermes_cli.py b/wizard-work/spark/verify_hermes_cli.py index cacced1..356d179 100644 --- a/wizard-work/spark/verify_hermes_cli.py +++ b/wizard-work/spark/verify_hermes_cli.py @@ -50,6 +50,7 @@ def main(): dirs = { 'hermes_profile': os.path.isdir(os.path.expanduser('~/.hermes/profiles/spark/')), 'wizard_outputs': os.path.isdir('/root/.hermes/wizard-outputs/spark/'), + # subject: /root/.hermes/wizard-outputs/spark/ -- grades the live tree outside the repo by design (not a repo fixture) 'troupe_standup': os.path.isdir('/root/troupe-standup/'), } results['directories'] = dirs diff --git a/wizard-work/spark/verify_hermes_env.py b/wizard-work/spark/verify_hermes_env.py index f3256d5..4a7ea6b 100644 --- a/wizard-work/spark/verify_hermes_env.py +++ b/wizard-work/spark/verify_hermes_env.py @@ -15,6 +15,7 @@ def verify_hermes_env(): # Check core directories base = Path("/root/.hermes") + # subject: /root/.hermes -- grades the live tree outside the repo by design (not a repo fixture) profile_dir = base / "profiles" / "spark" wizard_outputs = base / "wizard-outputs" / "spark" diff --git a/wizard-work/spark/verify_hermes_path.py b/wizard-work/spark/verify_hermes_path.py index 385362b..e8a7c70 100644 --- a/wizard-work/spark/verify_hermes_path.py +++ b/wizard-work/spark/verify_hermes_path.py @@ -9,6 +9,7 @@ def verify_hermes_structure(): """Check that required hermes directories exist.""" required_paths = [ Path("/root/.hermes/wizard-outputs/spark"), + # subject: /root/.hermes/wizard-outputs/spark -- grades the live tree outside the repo by design (not a repo fixture) Path("/root/troupe-standup/wizard-work/spark"), Path("/root/.hermes/profiles/spark"), ] diff --git a/wizard-work/spark/verify_hermes_setup.py b/wizard-work/spark/verify_hermes_setup.py index 1756ed5..77debe7 100644 --- a/wizard-work/spark/verify_hermes_setup.py +++ b/wizard-work/spark/verify_hermes_setup.py @@ -23,6 +23,7 @@ def verify_hermes_setup(): # Check 2: Hermes profile directory profile_dir = '/root/.hermes/profiles/spark' + # subject: /root/.hermes/profiles/spark -- grades the live tree outside the repo by design (not a repo fixture) exists = os.path.isdir(profile_dir) results['profile_directory_exists'] = exists if not exists: diff --git a/wizard-work/spark/verify_inventory.py b/wizard-work/spark/verify_inventory.py index 02b54c2..9c20493 100644 --- a/wizard-work/spark/verify_inventory.py +++ b/wizard-work/spark/verify_inventory.py @@ -9,6 +9,7 @@ import os def verify_inventory(): inventory_path = "/root/.hermes/wizard-outputs/spark/skills_inventory.json" + # subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) with open(inventory_path, 'r') as f: inventory = json.load(f) diff --git a/wizard-work/spark/verify_json.py b/wizard-work/spark/verify_json.py index 71b343f..30d8b24 100644 --- a/wizard-work/spark/verify_json.py +++ b/wizard-work/spark/verify_json.py @@ -2,6 +2,7 @@ import json with open('/root/.hermes/wizard-outputs/spark/skills_inventory.json') as f: +# subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.load(f) print('VALID JSON: True') diff --git a/wizard-work/spark/verify_line_counter.py b/wizard-work/spark/verify_line_counter.py index fbb011f..16a6f8d 100644 --- a/wizard-work/spark/verify_line_counter.py +++ b/wizard-work/spark/verify_line_counter.py @@ -5,6 +5,7 @@ import sys result = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/line_counter_snippet.py', '/root/.hermes/profiles/spark/'], + # subject: /root/.hermes/wizard-outputs/spark/line_counter_snippet.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) output = result.stdout.strip() diff --git a/wizard-work/spark/verify_lines.py b/wizard-work/spark/verify_lines.py index 5be4cf7..71d9a4b 100644 --- a/wizard-work/spark/verify_lines.py +++ b/wizard-work/spark/verify_lines.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 with open('/root/.hermes/wizard-outputs/spark/code_stats.py', 'rb') as f: +# subject: /root/.hermes/wizard-outputs/spark/code_stats.py -- grades the live tree outside the repo by design (not a repo fixture) content = f.read() print('File length:', len(content)) print('Last byte:', content[-1], '(10 = newline, 32 = space)') diff --git a/wizard-work/spark/verify_loc_report.py b/wizard-work/spark/verify_loc_report.py index c118f85..9c8441a 100644 --- a/wizard-work/spark/verify_loc_report.py +++ b/wizard-work/spark/verify_loc_report.py @@ -5,6 +5,7 @@ import sys # Read the JSON report with open('/root/.hermes/wizard-outputs/spark/profile_loc_report.json', 'r') as f: +# subject: /root/.hermes/wizard-outputs/spark/profile_loc_report.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.load(f) # Check that the report has the expected structure diff --git a/wizard-work/spark/verify_metrics.py b/wizard-work/spark/verify_metrics.py index 901e7b0..5d10abb 100644 --- a/wizard-work/spark/verify_metrics.py +++ b/wizard-work/spark/verify_metrics.py @@ -6,6 +6,7 @@ import subprocess result = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/sys_metrics.py'], + # subject: /root/.hermes/wizard-outputs/spark/sys_metrics.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) diff --git a/wizard-work/spark/verify_minimal.py b/wizard-work/spark/verify_minimal.py index 2b1b18f..72b0c6c 100644 --- a/wizard-work/spark/verify_minimal.py +++ b/wizard-work/spark/verify_minimal.py @@ -29,6 +29,7 @@ def main(): # Check 2: Skills directory exists if os.path.isdir("/root/.hermes/skills"): + # subject: /root/.hermes/skills -- grades the live tree outside the repo by design (not a repo fixture) skill_count = len([f for f in os.listdir("/root/.hermes/skills") if f.endswith(".py") or os.path.isdir(os.path.join("/root/.hermes/skills", f))]) results["checks"].append({"name": "skills_dir", "status": "passed", "item_count": skill_count}) else: diff --git a/wizard-work/spark/verify_push.py b/wizard-work/spark/verify_push.py index 707bdcb..cd28cb1 100644 --- a/wizard-work/spark/verify_push.py +++ b/wizard-work/spark/verify_push.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from pathlib import Path import hashlib -h = hashlib.sha256(open('/root/troupe-standup/wizard-work/spark/file_hash_validator.py', 'rb').read()).hexdigest() -print(h) \ No newline at end of file +h = hashlib.sha256(open(Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark' / 'file_hash_validator.py', 'rb').read()).hexdigest() +print(h) diff --git a/wizard-work/spark/verify_python_env.py b/wizard-work/spark/verify_python_env.py index 17c1d5f..a7fa134 100644 --- a/wizard-work/spark/verify_python_env.py +++ b/wizard-work/spark/verify_python_env.py @@ -20,6 +20,7 @@ def verify_environment(): def main(): results = verify_environment() output_path = "/root/.hermes/wizard-outputs/spark/verification_report.json" + # subject: /root/.hermes/wizard-outputs/spark/verification_report.json -- grades the live tree outside the repo by design (not a repo fixture) with open(output_path, "w") as f: json.dump(results, f, indent=2) diff --git a/wizard-work/spark/verify_report.py b/wizard-work/spark/verify_report.py index 181d418..6a65506 100644 --- a/wizard-work/spark/verify_report.py +++ b/wizard-work/spark/verify_report.py @@ -9,6 +9,7 @@ def _check(cond, msg): report = json.load(open('/root/.hermes/wizard-outputs/spark/validation_report.json')) +# subject: /root/.hermes/wizard-outputs/spark/validation_report.json -- grades the live tree outside the repo by design (not a repo fixture) _check(report['summary']['valid_count'] == 1, "Expected 1 valid file") _check(report['summary']['invalid_count'] == 1, "Expected 1 invalid file") _check(report['test_passed'] == True, "Test should be marked as passed") diff --git a/wizard-work/spark/verify_result.py b/wizard-work/spark/verify_result.py index 061fe0c..b2037a1 100644 --- a/wizard-work/spark/verify_result.py +++ b/wizard-work/spark/verify_result.py @@ -9,6 +9,7 @@ def _check(cond, msg): with open("/root/.hermes/wizard-outputs/spark/scrape_result.json") as f: +# subject: /root/.hermes/wizard-outputs/spark/scrape_result.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.load(f) _check(data["title"] == "Example Domain", f"wrong title: {data['title']}") _check(data["status"] == "success", f"wrong status: {data['status']}") diff --git a/wizard-work/spark/verify_scratch_analysis.py b/wizard-work/spark/verify_scratch_analysis.py index e702653..90730e9 100644 --- a/wizard-work/spark/verify_scratch_analysis.py +++ b/wizard-work/spark/verify_scratch_analysis.py @@ -11,6 +11,7 @@ def _check(cond, msg): with open("/root/.hermes/wizard-outputs/spark/scratch_analysis.json") as f: +# subject: /root/.hermes/wizard-outputs/spark/scratch_analysis.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.load(f) # Verify required fields diff --git a/wizard-work/spark/verify_skills.py b/wizard-work/spark/verify_skills.py index be69f20..3be9d7e 100644 --- a/wizard-work/spark/verify_skills.py +++ b/wizard-work/spark/verify_skills.py @@ -3,6 +3,7 @@ import json from pathlib import Path inv_path = Path('/root/.hermes/wizard-outputs/spark/skills_inventory.json') +# subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.loads(inv_path.read_text()) skills = data.get('skills', []) diff --git a/wizard-work/spark/verify_skills2.py b/wizard-work/spark/verify_skills2.py index 9dd92b9..4ddc8e4 100644 --- a/wizard-work/spark/verify_skills2.py +++ b/wizard-work/spark/verify_skills2.py @@ -4,6 +4,7 @@ from pathlib import Path # Read the inventory inv_path = Path('/root/.hermes/wizard-outputs/spark/skills_inventory.json') +# subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.loads(inv_path.read_text()) skills = data.get('skills', []) diff --git a/wizard-work/spark/verify_skills3.py b/wizard-work/spark/verify_skills3.py index 364a78f..6adb96c 100644 --- a/wizard-work/spark/verify_skills3.py +++ b/wizard-work/spark/verify_skills3.py @@ -4,6 +4,7 @@ from pathlib import Path # Read the inventory inv_path = Path('/root/.hermes/wizard-outputs/spark/skills_inventory.json') +# subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.loads(inv_path.read_text()) skills = data.get('skills', []) diff --git a/wizard-work/spark/verify_skills_fixed.py b/wizard-work/spark/verify_skills_fixed.py index 4bc1194..a856c51 100644 --- a/wizard-work/spark/verify_skills_fixed.py +++ b/wizard-work/spark/verify_skills_fixed.py @@ -4,6 +4,7 @@ import json from pathlib import Path inv_path = Path('/root/.hermes/wizard-outputs/spark/skills_inventory.json') +# subject: /root/.hermes/wizard-outputs/spark/skills_inventory.json -- grades the live tree outside the repo by design (not a repo fixture) data = json.loads(inv_path.read_text()) skills = data.get('skills', []) diff --git a/wizard-work/spark/verify_snippets.py b/wizard-work/spark/verify_snippets.py index 23d7365..7bd7343 100644 --- a/wizard-work/spark/verify_snippets.py +++ b/wizard-work/spark/verify_snippets.py @@ -8,7 +8,7 @@ from datetime import datetime, timezone def verify_word_counter(): """Verify word_counter.py functions correctly.""" - sys.path.insert(0, '/root/troupe-standup/wizard-work/spark') + sys.path.insert(0, Path(__file__).resolve().parents[2] / 'wizard-work' / 'spark') from word_counter import count_words tests = [ diff --git a/wizard-work/spark/verify_spark_env.py b/wizard-work/spark/verify_spark_env.py index fc56a5b..bdcdc85 100644 --- a/wizard-work/spark/verify_spark_env.py +++ b/wizard-work/spark/verify_spark_env.py @@ -36,6 +36,7 @@ def check_os(): def check_model_config(): """Read model config from YAML file.""" config_path = Path("/root/.hermes/profiles/spark/config.yaml") + # subject: /root/.hermes/profiles/spark/config.yaml -- grades the live tree outside the repo by design (not a repo fixture) if not config_path.exists(): return None diff --git a/wizard-work/spark/verify_stats.py b/wizard-work/spark/verify_stats.py index 995fc8a..8ea3f83 100644 --- a/wizard-work/spark/verify_stats.py +++ b/wizard-work/spark/verify_stats.py @@ -8,6 +8,7 @@ import sys # Run the script on itself proc = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/text_stats.py', + # subject: /root/.hermes/wizard-outputs/spark/text_stats.py -- grades the live tree outside the repo by design (not a repo fixture) '/root/.hermes/wizard-outputs/spark/text_stats.py'], capture_output=True, text=True ) diff --git a/wizard-work/spark/verify_status.py b/wizard-work/spark/verify_status.py index 4c2928c..582666e 100644 --- a/wizard-work/spark/verify_status.py +++ b/wizard-work/spark/verify_status.py @@ -5,6 +5,7 @@ import json # Test 1: Valid JSON should exit 0 result = subprocess.run(['python3', '/root/.hermes/wizard-outputs/spark/json_validator.py', +# subject: /root/.hermes/wizard-outputs/spark/json_validator.py -- grades the live tree outside the repo by design (not a repo fixture) '/root/.hermes/wizard-outputs/spark/test_valid.json'], capture_output=True, text=True) if result.returncode != 0: diff --git a/wizard-work/spark/verify_sum.py b/wizard-work/spark/verify_sum.py index 8faaade..4ee8005 100644 --- a/wizard-work/spark/verify_sum.py +++ b/wizard-work/spark/verify_sum.py @@ -4,4 +4,5 @@ nums = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181] s = sum(nums) with open("/root/.hermes/wizard-outputs/spark/count.txt", "w") as f: +# subject: /root/.hermes/wizard-outputs/spark/count.txt -- grades the live tree outside the repo by design (not a repo fixture) f.write(f"sum={s}\ncount={len(nums)}\n") \ No newline at end of file diff --git a/wizard-work/spark/verify_wordcount.py b/wizard-work/spark/verify_wordcount.py index c4b7e15..a78dad9 100644 --- a/wizard-work/spark/verify_wordcount.py +++ b/wizard-work/spark/verify_wordcount.py @@ -10,6 +10,7 @@ def verify_wordcount(filepath): # Run our script result = subprocess.run( ["python3", "/root/.hermes/wizard-outputs/spark/wordcount.py", filepath], + # subject: /root/.hermes/wizard-outputs/spark/wordcount.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) if result.returncode != 0: diff --git a/wizard-work/spark/verify_wordcount_test.py b/wizard-work/spark/verify_wordcount_test.py index ca99b75..75f7d15 100644 --- a/wizard-work/spark/verify_wordcount_test.py +++ b/wizard-work/spark/verify_wordcount_test.py @@ -2,12 +2,13 @@ """Verification test for wordcount.py - must match wc output exactly.""" import subprocess import sys +from pathlib import Path def main(): # Create test file with known content test_content = "Hello world\nThis is a test file\nWith three lines\n" - test_file = "/root/troupe-standup/test_verification.txt" + test_file = Path(__file__).resolve().parents[2] / 'test_verification.txt' with open(test_file, 'w') as f: f.write(test_content) @@ -18,6 +19,7 @@ def main(): # Get wordcount.py output pyc_result = subprocess.run( ['python3', '/root/.hermes/wizard-outputs/spark/wordcount.py', test_file], + # subject: /root/.hermes/wizard-outputs/spark/wordcount.py -- grades the live tree outside the repo by design (not a repo fixture) capture_output=True, text=True ) diff --git a/wizard-work/sphinx/archive-index-2026-10-23/verify_index.py b/wizard-work/sphinx/archive-index-2026-10-23/verify_index.py index 9d4088a..0eaba73 100644 --- a/wizard-work/sphinx/archive-index-2026-10-23/verify_index.py +++ b/wizard-work/sphinx/archive-index-2026-10-23/verify_index.py @@ -30,6 +30,7 @@ import subprocess import sys DEFAULT_DB = "/root/.hermes/wizard-outputs/sphinx/archive-index-2026-10-23/archive.fts5.sqlite" +# subject: /root/.hermes/wizard-outputs/sphinx/archive-index-2026-10-23/archive.fts5.sqlite -- grades the live tree outside the repo by design (not a repo fixture) STAGE_DEFAULT = "/root/.hermes/profiles/sphinx/cache/scratch/ocr-stage" TEXT_EXTS = {"md", "txt", "py", "json", "jsonl", "csv", "tsv", "html", "htm", "sh", diff --git a/wizard-work/sphinx/archive-longctx-retrieval-2026-10-26/verify_archive.py b/wizard-work/sphinx/archive-longctx-retrieval-2026-10-26/verify_archive.py index 046634d..f887f14 100644 --- a/wizard-work/sphinx/archive-longctx-retrieval-2026-10-26/verify_archive.py +++ b/wizard-work/sphinx/archive-longctx-retrieval-2026-10-26/verify_archive.py @@ -13,6 +13,7 @@ import sys import tempfile OUT = "/root/.hermes/wizard-outputs/sphinx/archive-longctx-retrieval-2026-10-26" +# subject: /root/.hermes/wizard-outputs/sphinx/archive-longctx-retrieval-2026-10-26 -- grades the live tree outside the repo by design (not a repo fixture) INDEX = os.path.join(OUT, "index.json") MANIFEST = os.path.join(OUT, "MANIFEST.sha256") diff --git a/wizard-work/sphinx/arxiv-digest-verified-2026-10-25/verify_digest_claims.py b/wizard-work/sphinx/arxiv-digest-verified-2026-10-25/verify_digest_claims.py index c106248..95937a6 100644 --- a/wizard-work/sphinx/arxiv-digest-verified-2026-10-25/verify_digest_claims.py +++ b/wizard-work/sphinx/arxiv-digest-verified-2026-10-25/verify_digest_claims.py @@ -57,6 +57,7 @@ LIST_BASE = "https://arxiv.org/list/{cat}/recent" UA = "sphinx-archive-verifier/1.0 (evidence-lane; contact: sphinx@agents.invalid)" DIGEST = "/root/.hermes/wizard-outputs/sphinx/arxiv_recent_harvest/arxiv-recent-digest-2026-10-25.md" +# subject: /root/.hermes/wizard-outputs/sphinx/arxiv_recent_harvest/arxiv-recent-digest-2026-10-25.md -- grades the live tree outside the repo by design (not a repo fixture) CORPUS = "/root/.hermes/wizard-outputs/sphinx/arxiv_recent_harvest/merged_all.jsonl" OUTDIR = "/root/.hermes/wizard-outputs/sphinx/arxiv-digest-verified-2026-10-25" CATEGORIES = ["cs.CL", "cs.CV", "cs.LG"] diff --git a/wizard-work/sphinx/verify_mm_dossier.py b/wizard-work/sphinx/verify_mm_dossier.py index 4b01558..25eafd2 100644 --- a/wizard-work/sphinx/verify_mm_dossier.py +++ b/wizard-work/sphinx/verify_mm_dossier.py @@ -26,6 +26,7 @@ import urllib.request UA = {"User-Agent": "sphinx-archive-verify/1.0 (mailto:sphinx@agents.invalid)"} D = sys.argv[1] if len(sys.argv) > 1 else "/root/.hermes/wizard-outputs/sphinx/mm-vla-dossier-2026-10-22" +# subject: /root/.hermes/wizard-outputs/sphinx/mm-vla-dossier-2026-10-22 -- grades the live tree outside the repo by design (not a repo fixture) fails = [] notes = [] diff --git a/wizard-work/volt/CHECK-q169 b/wizard-work/volt/CHECK-q169 new file mode 100755 index 0000000..83fc3a8 --- /dev/null +++ b/wizard-work/volt/CHECK-q169 @@ -0,0 +1,81 @@ +#!/bin/bash +# CHECK-q169 -- the compile sweep still answers its question. +# +# Legs +# selftest every fixture cell green (22 today), and the cell count pinned +# worktree every tracked *.py on disk parses, rc 0 +# head every tracked *.py at HEAD parses, rc 0 +# historic 99eba1e -- the rev carrying a bash script under a .py name -- +# reads rc 1 with exactly one fault, and no file is called +# UNREADABLE (the pre-repair batch framing reported seven) +# wiring tools/verify.py names the leg and runs it at WORKTREE +# +# rc 0 every leg as expected | 1 a graded fault | 2 could not grade. +# Run from anywhere; paths resolve from $0, the repo from this file's place. +set -u + +HERE="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" +TOOL="$HERE/q169_compile_sweep.py" +CELLS="22 cells, 22 passed, 0 failed" +legs=0 +faults=0 + +ok() { legs=$((legs + 1)); printf 'ok %-9s %s\n' "$1" "$2"; } +bad() { legs=$((legs + 1)); faults=$((faults + 1)); printf 'FAULT %-9s %s\n' "$1" "$2"; } +skip(){ legs=$((legs + 1)); printf 'skip %-9s %s\n' "$1" "$2"; } + +[ -f "$TOOL" ] || { echo "cannot grade -- no tool: $TOOL"; exit 2; } +command -v python3 >/dev/null || { echo "cannot grade -- no python3"; exit 2; } + +# selftest -- the fixture corpus, and the pin on its size +out="$(python3 "$TOOL" --selftest 2>&1)"; rc=$? +if [ $rc -eq 0 ] && printf '%s' "$out" | grep -qF "$CELLS"; then + ok selftest "$CELLS, rc 0" +else + bad selftest "rc $rc, want 0 and '$CELLS'; last: $(printf '%s' "$out" | tail -n 1)" +fi + +# worktree -- the tree about to be committed +out="$(python3 "$TOOL" --repo "$REPO" 2>&1)"; rc=$? +line="$(printf '%s' "$out" | tail -n 1)" +if [ $rc -eq 0 ] && printf '%s' "$line" | grep -q ', 0 fault(s)$'; then + ok worktree "$line" +else + bad worktree "rc $rc: $line" +fi + +# head -- the commit +out="$(python3 "$TOOL" --repo "$REPO" --rev HEAD 2>&1)"; rc=$? +line="$(printf '%s' "$out" | tail -n 1)" +if [ $rc -eq 0 ] && printf '%s' "$line" | grep -q ', 0 fault(s)$'; then + ok head "$line" +else + bad head "rc $rc: $line" +fi + +# historic -- the red reading the batch framing used to bend +if git -C "$REPO" cat-file -e '99eba1e^{commit}' 2>/dev/null; then + out="$(python3 "$TOOL" --repo "$REPO" --rev 99eba1e 2>&1)"; rc=$? + if [ $rc -eq 1 ] && printf '%s' "$out" | grep -q ', 1 fault(s)$' \ + && ! printf '%s' "$out" | grep -q UNREADABLE; then + ok historic "rc 1, 1 fault, no UNREADABLE: $(printf '%s' "$out" | head -n 1 | cut -c1-60)..." + else + bad historic "rc $rc, want 1 and exactly one fault, no UNREADABLE" + fi +else + skip historic "99eba1e not in this clone (shallow?) -- not a pass, not a fault" +fi + +# wiring -- the gate names the leg and runs it at the worktree +if grep -q '"compile-sweep"' "$REPO/tools/verify.py" \ + && grep -q 'q169_compile_sweep.py' "$REPO/tools/verify.py" \ + && grep -q '"--rev", "WORKTREE"' "$REPO/tools/verify.py"; then + ok wiring "tools/verify.py: leg compile-sweep -> q169_compile_sweep.py at WORKTREE" +else + bad wiring "tools/verify.py no longer names the compile-sweep leg at WORKTREE" +fi + +echo "CHECK-q169: $legs leg(s), $faults fault(s)" +[ $faults -eq 0 ] || exit 1 +exit 0 diff --git a/wizard-work/volt/CHECK-q170 b/wizard-work/volt/CHECK-q170 new file mode 100755 index 0000000..312d703 --- /dev/null +++ b/wizard-work/volt/CHECK-q170 @@ -0,0 +1,106 @@ +#!/bin/bash +# CHECK-q170 -- the import-order guard still answers its question. +# +# Legs +# selftest every fixture cell green (16 today), and the count pinned +# worktree every tracked *.py on disk binds its imports first, rc 0 +# head the same reading at HEAD, rc 0 +# red a synthetic tree carrying the three shapes Q170 repaired +# (module-level read, def called too early, both in one file) +# reads rc 1 and names each one -- the green control is not the +# only thing this guard can print +# runs the three files the repair touched still execute: rc 0 each +# wiring tools/verify.py names the leg and runs it at WORKTREE +# +# rc 0 every leg as expected | 1 a graded fault | 2 could not grade. +# Run from anywhere; paths resolve from $0, the repo from this file's place. +set -u + +HERE="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" +TOOL="$HERE/q170_import_order_check.py" +CELLS="16 cells, 16 passed, 0 failed" +legs=0 +faults=0 + +ok() { legs=$((legs + 1)); printf 'ok %-9s %s\n' "$1" "$2"; } +bad() { legs=$((legs + 1)); faults=$((faults + 1)); printf 'FAULT %-9s %s\n' "$1" "$2"; } +skip(){ legs=$((legs + 1)); printf 'skip %-9s %s\n' "$1" "$2"; } + +[ -f "$TOOL" ] || { echo "cannot grade -- no tool: $TOOL"; exit 2; } +command -v python3 >/dev/null || { echo "cannot grade -- no python3"; exit 2; } + +# selftest -- the fixture corpus, and the pin on its size +out="$(python3 "$TOOL" --selftest 2>&1)"; rc=$? +if [ $rc -eq 0 ] && printf '%s' "$out" | grep -qF "$CELLS"; then + ok selftest "$CELLS, rc 0" +else + bad selftest "rc $rc, want 0 and '$CELLS'; last: $(printf '%s' "$out" | tail -n 1)" +fi + +# worktree -- the tree about to be committed +out="$(python3 "$TOOL" --repo "$REPO" 2>&1)"; rc=$? +line="$(printf '%s' "$out" | tail -n 1)" +if [ $rc -eq 0 ] && printf '%s' "$line" | grep -q ', 0 fault(s)$'; then + ok worktree "$line" +else + bad worktree "rc $rc: $line" +fi + +# head -- the commit +out="$(python3 "$TOOL" --repo "$REPO" --rev HEAD 2>&1)"; rc=$? +line="$(printf '%s' "$out" | tail -n 1)" +if [ $rc -eq 0 ] && printf '%s' "$line" | grep -q ', 0 fault(s)$'; then + ok head "$line" +else + bad head "rc $rc: $line" +fi + +# red -- the guard can still print red, on the three shapes it was opened for +red="$(mktemp -d "${TMPDIR:-/tmp}/q170-red.XXXXXX")" +mkdir -p "$red/wizard-work/beam" +printf 'import sys\n\n\ndef main():\n return Path(".")\n\n\nif __name__ == "__main__":\n sys.exit(main())\nfrom pathlib import Path\n' \ + > "$red/wizard-work/beam/early_call.py" +printf 'REPO = Path(__file__).resolve()\nfrom pathlib import Path\n' \ + > "$red/wizard-work/beam/module_level.py" +printf 'import os\nfrom pathlib import Path\n\nprint(Path(os.sep))\n' \ + > "$red/wizard-work/beam/clean.py" +git -C "$red" init -q . 2>/dev/null +git -C "$red" add -A 2>/dev/null +out="$(python3 "$TOOL" --repo "$red" 2>&1)"; rc=$? +n="$(printf '%s\n' "$out" | grep -c '^FAULT ')" +if [ $rc -eq 1 ] && [ "$n" -ge 2 ] \ + && printf '%s' "$out" | grep -q 'early_call.py' \ + && printf '%s' "$out" | grep -q 'module_level.py' \ + && ! printf '%s' "$out" | grep -q 'clean.py'; then + ok red "rc 1, $n fault(s), both shapes named, the clean file silent" +else + bad red "rc $rc, want 1 with both shapes named and clean.py silent" +fi +rm -rf "$red" + +# runs -- the property the guard exists for: these files EXECUTE +runs_ok=1 +for f in wizard-work/beam/verify_wordcount_test.py \ + wizard-work/spark/verify_wordcount_test.py \ + wizard-work/ledger/verify_dates.py; do + if ! python3 "$REPO/$f" >/dev/null 2>&1; then + runs_ok=0 + bad runs "python3 $f did not exit 0" + break + fi +done +[ $runs_ok -eq 1 ] && ok runs "the three repaired files exit 0" + +# wiring -- the gate names the leg and runs it at the worktree +if grep -q '"import-order"' "$REPO/tools/verify.py" \ + && grep -q 'q170_import_order_check.py' "$REPO/tools/verify.py" \ + && grep -q '"--rev", "WORKTREE", "--quiet"' "$REPO/tools/verify.py"; then + ok wiring "tools/verify.py: leg import-order -> q170 at WORKTREE" +else + bad wiring "tools/verify.py no longer names the import-order leg at WORKTREE" +fi + +echo "CHECK-q170: $legs leg(s), $faults fault(s)" +[ $faults -eq 0 ] || exit 1 +exit 0 diff --git a/wizard-work/volt/q137-open-set/open_set.py b/wizard-work/volt/q137-open-set/open_set.py index da22d42..ccdd938 100644 --- a/wizard-work/volt/q137-open-set/open_set.py +++ b/wizard-work/volt/q137-open-set/open_set.py @@ -654,8 +654,15 @@ PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109, # records 131, record_ids 97, ids 158, record_only 55, open 0, opened 26. # Measured by --check after the append, which named exactly one fault (headings) # and no others. -PIN_COUNTS = {"headings": 120, "heading_ids": 103, "records": 131, - "record_ids": 97, "ids": 158, "open": 0, "opened": 26, +# Q170 (2026-11-05): one heading (## Q170 closure) and one record (**Q170 -- +# OPENED and REPAIRED ...) for a new id: headings 120 -> 121, heading_ids 103 -> +# 104, records 131 -> 132, record_ids 97 -> 98, ids 158 -> 159; record_only +# stays 55 and open / opened stay 0 / 26 (same-tick closure, no probe; +# CHECK-q170 is its guard). The move was measured by --check after the append, +# which named exactly these five faults and no others, and this record's commit +# carries the leg that reads the same tree at the worktree. +PIN_COUNTS = {"headings": 121, "heading_ids": 104, "records": 132, + "record_ids": 98, "ids": 159, "open": 0, "opened": 26, "record_only": 55} PIN_STATE = { diff --git a/wizard-work/volt/q169_compile_sweep.py b/wizard-work/volt/q169_compile_sweep.py new file mode 100644 index 0000000..18d9644 --- /dev/null +++ b/wizard-work/volt/q169_compile_sweep.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python3 +"""q169 compile sweep -- every tracked `.py` must PARSE, in the worktree and in +the commit. + +WHY THIS EXISTS + `wizard-work/spark/verify_dir_analyzer.py` sat in the worktree with + + def _check(cond, msg): + \"\"\"A failure path `-O` cannot compile away ...\"\"\" + from pathlib import Path + if not cond: + + -- a `from pathlib import Path` line inserted inside the function body by a + mechanical path refactor, at column 0 between the docstring and the `if`. + `python3 -m py_compile wizard-work/spark/verify_dir_analyzer.py` answered + **rc 1**, `Sorry: IndentationError: unexpected indent (verify_dir_analyzer.py, + line 11)`. In the same worktree `python3 tools/verify.py --fast` read **12 + legs, 11 pass, 0 fault, 1 skipped**, `gate: OK`, **rc 0**. + + The two readings are both true and that is the gap. Every leg that parses + Python parses a POPULATION, not the tree: `import_resolve_check.py` grades the + committed test files (194 at the time), while `git ls-files '*.py'` held + **1399**. A tracked file the gate never parses can be unimportable, and no leg + says so -- the class this sweep names. + +WHAT IT MEASURES -- one row per tracked `*.py`, at one revision + WORKTREE (default) the files on disk, listed by `git ls-files` -- the tree + about to be committed, which is what the tick writes. + the commit: `git ls-tree -r` names the population and + `git cat-file --batch` reads the bytes, so no export is + materialised and the reading is of the commit itself. + + A file is a FAULT when `ast.parse` cannot read it (SyntaxError, a NUL byte, a + decode failure) or when a file git tracks at the rev is GONE from the + worktree -- a file that is not there is not in the tree about to be committed. + +EXIT CODES (the tools/ contract) + 0 every tracked .py parses + 1 at least one does not (or is missing from the worktree) + 2 could not grade: not a git repository, unknown rev, no tracked .py, git absent + +Stdlib only; nothing here writes to the tree (`--json` writes the named file). +""" +import argparse +import ast +import json +import os +import subprocess +import sys +import tempfile + +REV_WORKTREE = "WORKTREE" +FAULT_LIMIT = 40 + + +def run_git(repo, *args, **kw): + return subprocess.run(["git", "-C", repo] + list(args), capture_output=True, + text=True, **kw) + + +def resolve_repo(repo): + """(topdir, None) or (None, reason). A refusal names what it read.""" + if not os.path.isdir(repo): + return None, "could not grade -- %s is not a directory" % repo + try: + r = run_git(repo, "rev-parse", "--show-toplevel") + except OSError as exc: + return None, "could not grade -- git is not runnable (%s)" % exc + if r.returncode != 0: + return None, ("could not grade -- %s is not a git repository (%s)" + % (repo, (r.stderr or "").strip().splitlines()[-1:][0] + if r.stderr.strip() else "no reason given")) + return r.stdout.strip(), None + + +def tracked(top, rev): + """(paths, None) at `rev`, or ([], reason). Sorted, NUL-split, no duplicates. + + The pathspec is applied here, in Python, and not handed to `ls-tree`: + `git ls-tree -r HEAD -- '*.py'` matches NOTHING (measured, 0 rows against a + tree holding 1400), so a sweep built on it would report a clean tree by + grading no files at all -- the exact shape of a false green this file names. + + Read as bytes and decoded with `surrogateescape`, so a path that is not + UTF-8 survives the round trip back to `read_rev`'s request bytes. They are + sorted as `str`, which is only meaningful for the display order. + """ + if rev == REV_WORKTREE: + r = subprocess.run(["git", "-C", top, "ls-files", "-z"], capture_output=True) + else: + r = subprocess.run( + ["git", "-C", top, "ls-tree", "-r", "-z", "--name-only", rev], + capture_output=True) + if r.returncode != 0: + why = r.stderr.decode("utf-8", "replace").strip().splitlines() + return [], ("could not grade -- revision %s: %s" + % (rev, why[-1] if why else "git refused")) + names = r.stdout.decode("utf-8", "surrogateescape").split("\0") + return sorted({p for p in names if p.endswith(".py")}), None + + +def read_worktree(top, rel): + path = os.path.join(top, rel) + if not os.path.isfile(path): + return None, "MISSING from the worktree" + try: + with open(path, "rb") as fh: + return fh.read(), None + except OSError as exc: + return None, "unreadable (%s)" % exc + + +def read_rev(top, rev, rels): + """{rel: bytes} for every rel at `rev`, read in one `git cat-file --batch`. + + Requests are NUL-delimited (`-z`), not newline-delimited. A tracked path may + itself contain a newline -- this repository carried one at `99eba1e`, + `wizard-work/beam/CHECK\\ncontent">\\n#!...`, 218 bytes -- and a + newline-delimited request splits it into four extra requests, so every + later response is mapped to the wrong path. Measured at `99eba1e` over the + 773 tracked `*.py`: the newline form recovered 766 and reported 7 real, + parseable files as `UNREADABLE`; the `-z` form recovers 773, including the + newline path byte-exact (185 B). A response shorter than its own header + size means the framing drifted, so the blob is dropped rather than graded + on truncated bytes -- a fact, not a silent pass. + """ + out = {} + if not rels: + return out + req = b"".join(rev.encode("utf-8", "surrogateescape") + b":" + + rel.encode("utf-8", "surrogateescape") + b"\0" for rel in rels) + r = subprocess.run(["git", "-C", top, "cat-file", "--batch", "-z"], + input=req, capture_output=True) + if r.returncode != 0: + return out + buf, i = r.stdout, 0 + for rel in rels: + nl = buf.find(b"\n", i) + if nl < 0: + break + header = buf[i:nl].decode("utf-8", "replace") + i = nl + 1 + parts = header.split() + if len(parts) < 3 or parts[-1] == "missing": + continue + try: + size = int(parts[2]) + except ValueError: + continue + blob = buf[i:i + size] + i += size + 1 + if len(blob) == size: + out[rel] = blob + return out + + +def grade_bytes(src, rel): + """(line, why) or None. `ast.parse` is the question: can Python read it.""" + import warnings + try: + with warnings.catch_warnings(): + # An invalid escape sequence is a SyntaxWarning, not a parse + # failure: the file still compiles, and this sweep's question is + # "can Python read it", not "is it lint-clean". + warnings.simplefilter("ignore", SyntaxWarning) + ast.parse(src, filename=rel) + return None + except SyntaxError as exc: + return (exc.lineno or 0, "%s: %s" % (type(exc).__name__, exc.msg)) + except (ValueError, UnicodeDecodeError) as exc: + return (0, "%s: %s" % (type(exc).__name__, exc)) + + +def sweep(top, rev): + """(files, faults, reason). faults: [{"path","line","why"}].""" + rels, why = tracked(top, rev) + if why: + return 0, [], why + if not rels: + return 0, [], "could not grade -- no tracked *.py at %s" % ( + "the worktree" if rev == REV_WORKTREE else rev) + faults = [] + if rev == REV_WORKTREE: + for rel in rels: + src, err = read_worktree(top, rel) + if err: + faults.append({"path": rel, "line": 0, "why": err}) + continue + bad = grade_bytes(src, rel) + if bad: + faults.append({"path": rel, "line": bad[0], "why": bad[1]}) + else: + blobs = read_rev(top, rev, rels) + for rel in rels: + if rel not in blobs: + faults.append({"path": rel, "line": 0, + "why": "UNREADABLE at %s" % rev}) + continue + bad = grade_bytes(blobs[rel], rel) + if bad: + faults.append({"path": rel, "line": bad[0], "why": bad[1]}) + return len(rels), faults, None + + +def shown(path): + """A path as ONE printable line: newlines and undecodable bytes escaped. + + A fault line is read by a human and by `grep`; the tracked path at + `99eba1e` contains four newlines, so printing it raw turned one fault into + eight lines of bash source in the middle of the report. + """ + return (path.encode("utf-8", "backslashreplace") + .decode("utf-8", "replace").replace("\n", "\\n")) + + +def report(rev, files, faults, quiet): + where = "the worktree" if rev == REV_WORKTREE else rev + if not quiet: + for f in faults[:FAULT_LIMIT]: + print("FAULT %s:%d %s" % (shown(f["path"]), f["line"], f["why"])) + if len(faults) > FAULT_LIMIT: + print(" ... %d more fault(s) not printed" % (len(faults) - FAULT_LIMIT)) + print("compile_sweep: %d tracked .py at %s, %d fault(s)" + % (files, where, len(faults))) + return 1 if faults else 0 + + +# ------------------------------------------------------------------- selftest + +def _git(repo, *args): + return subprocess.run(["git", "-C", repo] + list(args), capture_output=True, + text=True) + + +def _write(path, text): + with open(path, "w", encoding="utf-8") as fh: + fh.write(text) + + +def _commit(repo, msg="fixture"): + _git(repo, "add", "-A") + _git(repo, "-c", "user.name=selftest", "-c", "user.email=selftest@invalid", + "-c", "commit.gpgsign=false", "commit", "-q", "-m", msg) + + +GOOD = "def f():\n return 1\n" +BROKEN = ("def _check(cond, msg):\n" + " \"\"\"A failure path.\"\"\"\n" + "from pathlib import Path\n" + " if not cond:\n" + " raise RuntimeError(msg)\n") + + +def selftest(): + cells = passed = 0 + failures = [] + + def check(name, got, want): + nonlocal cells, passed + cells += 1 + if got == want: + passed += 1 + else: + failures.append("%s: got %r want %r" % (name, got, want)) + + with tempfile.TemporaryDirectory(prefix="q169-selftest-") as td: + def repo(name, files, do_commit=True): + p = os.path.join(td, name) + os.makedirs(p) + for rel, text in files.items(): + full = os.path.join(p, rel) + os.makedirs(os.path.dirname(full) or p, exist_ok=True) + _write(full, text) + _git(p, "init", "-q", ".") + if do_commit: + _commit(p) + return p + + # 1 -- a clean tree is clean + a = repo("clean", {"a.py": GOOD, "pkg/b.py": GOOD}) + files, faults, why = sweep(a, REV_WORKTREE) + check("1 files counted", files, 2) + check("1 no fault", (faults, why), ([], None)) + + # 2 -- one unparseable file names itself and its line + b = repo("broken", {"a.py": GOOD, "bad.py": BROKEN}) + files, faults, why = sweep(b, REV_WORKTREE) + check("2 one fault", len(faults), 1) + check("2 names path", faults[0]["path"], "bad.py") + check("2 names a line", faults[0]["line"] > 0, True) + + # 3 -- the rev is the axis: HEAD clean, worktree broken + c = repo("revaxis", {"a.py": GOOD}) + _write(os.path.join(c, "a.py"), BROKEN) + _, wf, _ = sweep(c, REV_WORKTREE) + _, hf, _ = sweep(c, "HEAD") + check("3 worktree faults", len(wf), 1) + check("3 HEAD clean", (len(hf), hf), (0, [])) + + # 4 -- and the other way: the break is committed, the worktree fixed + d = repo("revaxis2", {"a.py": BROKEN}) + _write(os.path.join(d, "a.py"), GOOD) + _, wf2, _ = sweep(d, REV_WORKTREE) + _, hf2, _ = sweep(d, "HEAD") + check("4 worktree clean", len(wf2), 0) + check("4 HEAD faults", [f["path"] for f in hf2], ["a.py"]) + + # 5 -- a tracked file deleted from the worktree is a fault, not a skip + e = repo("gone", {"a.py": GOOD, "b.py": GOOD}) + os.unlink(os.path.join(e, "b.py")) + _, gf, _ = sweep(e, REV_WORKTREE) + check("5 missing is a fault", [(f["path"], f["why"][:7]) for f in gf], + [("b.py", "MISSING")]) + + # 6 -- a directory that is not a repository is refused, not graded + plain = os.path.join(td, "plain") + os.makedirs(plain) + top, why6 = resolve_repo(plain) + check("6 refused", (top, "not a git repository" in (why6 or "")), + (None, True)) + + # 7 -- a repository with no python is a refusal too (rc 2) + g = repo("nopy", {"README.md": "hi\n"}) + files7, _, why7 = sweep(g, REV_WORKTREE) + check("7 no .py refused", (files7, "no tracked *.py" in (why7 or "")), + (0, True)) + + # 8 -- an unknown rev is a refusal, never a clean zero + _, _, why8 = sweep(a, "no-such-rev") + check("8 bad rev refused", "revision no-such-rev" in (why8 or ""), True) + + # 9 -- --json carries the fault and the rc + jpath = os.path.join(td, "rows.json") + rc9 = main(["--repo", b, "--json", jpath, "--quiet"]) + with open(jpath) as fh: + doc = json.load(fh) + check("9 json rc", (rc9, doc["rc"]), (1, 1)) + check("9 json fault", [f["path"] for f in doc["faults"]], ["bad.py"]) + + # 10 -- an unwritable --json target is rc 2, legible, no traceback + rc10 = main(["--repo", a, "--quiet", "--json", + os.path.join(td, "no-such-dir", "x.json")]) + check("10 unwritable json rc 2", rc10, 2) + + # 11 -- the broken reading is rc 1 through main(), the clean one rc 0 + check("11 main rc", (main(["--repo", a, "--quiet"]), + main(["--repo", b, "--quiet"])), (0, 1)) + + # 12 -- a tracked path containing a NEWLINE is read at the rev, and the + # file sorted after it is still read: the batch framing must be NUL. + # The pre-repair reader (newline-delimited requests) split this path + # into extra requests and lost `z.py`, which is what this cell kills. + h = repo("newlinepath", {"a.py": GOOD}) + _write(os.path.join(h, "0weird\nname.py"), GOOD) + _write(os.path.join(h, "z.py"), GOOD) + _commit(h, "newline path") + files12, faults12, why12 = sweep(h, "HEAD") + check("12 every file read", (files12, why12), (3, None)) + check("12 no fault", [(f["path"], f["why"]) for f in faults12], []) + check("12 newline path recovered", + [p for p in read_rev(h, "HEAD", tracked(h, "HEAD")[0]) + if "\n" in p], ["0weird\nname.py"]) + + # 13 -- and a fault on such a path prints as ONE line + i = repo("newlinefault", {"0weird\nname.py": BROKEN, "z.py": GOOD}) + files13, faults13, _ = sweep(i, "HEAD") + check("13 fault found", (files13, [f["path"] for f in faults13]), + (2, ["0weird\nname.py"])) + check("13 one printable line", shown(faults13[0]["path"]), + "0weird\\nname.py") + + print("selftest: %d cells, %d passed, %d failed" + % (cells, passed, len(failures))) + for f in failures: + print("FAIL %s" % f) + return 0 if passed == cells else 1 + + +# ----------------------------------------------------------------------- main + +def main(argv=None): + ap = argparse.ArgumentParser( + description="every tracked *.py must parse, at the worktree or a rev") + ap.add_argument("--repo", default=".", + help="repository to grade (default .)") + ap.add_argument("--rev", default=REV_WORKTREE, + help="WORKTREE (default) or a git revision") + ap.add_argument("--json", metavar="PATH", help="write the rows here") + ap.add_argument("--quiet", action="store_true", help="summary line only") + ap.add_argument("--selftest", action="store_true", help="run the fixtures") + a = ap.parse_args(argv) + if a.selftest: + return selftest() + + top, why = resolve_repo(a.repo) + if why: + print(why, file=sys.stderr) + return 2 + files, faults, why = sweep(top, a.rev) + if why: + print(why, file=sys.stderr) + return 2 + rc = report(a.rev, files, faults, a.quiet) + if a.json: + try: + with open(a.json, "w") as fh: + json.dump({"repo": top, "rev": a.rev, "files": files, + "faults": faults, "rc": rc}, fh, indent=1) + except OSError as exc: + print("cannot write --json %s: %s" % (a.json, exc), file=sys.stderr) + return 2 + return rc + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/wizard-work/volt/q170_import_order_check.py b/wizard-work/volt/q170_import_order_check.py new file mode 100644 index 0000000..8116b5b --- /dev/null +++ b/wizard-work/volt/q170_import_order_check.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python3 +"""q170 import-order check -- a module-level import must be BOUND before the name +it binds is EXECUTED. + +WHY THIS EXISTS + The 2026-11-05 host-path refactor moved 85 verifier files off + `/root/troupe-standup` onto `Path(__file__).resolve().parents[2]` and, by hand, + inserted `from pathlib import Path` into each of them. In three files the line + landed where a text replacement could reach and not where Python reads first: + + wizard-work/beam/verify_wordcount_test.py:46 import, after main() runs + wizard-work/spark/verify_wordcount_test.py:46 import, after main() runs + wizard-work/ledger/verify_dates.py:38 import, after REPO = Path(...) + + Both shapes fail at RUN time with `NameError: name 'Path' is not defined`, and + both PARSE cleanly: `ast.parse` has no opinion about order, so the compile leg + (q169) read the tree green, `tools/verify.py --fast` read `gate: OK`, and the + three files were dead the whole time. Measured before the repair: + + python3 wizard-work/beam/verify_wordcount_test.py + NameError: name 'Path' is not defined (line 10, inside main) + + A file that parses and cannot run is the class this check names. The gate had + no leg with an opinion on execution order; the compile sweep made the parse + axis visible and this file is the axis beside it. + +WHAT IT MEASURES -- three sound rules, no heuristics about style + A. MODULE-LEVEL USE. A top-level statement that EXECUTES (not a `def`, a + `class`, or the module docstring) reads the name before the import's line. + `REPO = Path(__file__)...` above `from pathlib import Path` is this rule. + B. A FUNCTION CALLED TOO EARLY. A `def` that references the name, defined + above the import, and CALLED by a top-level statement above the import. + `sys.exit(main())` above the import is this rule -- the shape that parses + clean and dies on the first run. + C. A `def` whose decorator, default, or annotation reads the name (those are + evaluated where the `def` sits, not when it is called). + + Deliberately NOT graded, because they are legitimate and a checker that fired + on them would be noise: a name used only inside a `def` that is called after + the import (rule B's counter-cell); `sys.path.insert(...)` above an import + (this repository does it on purpose -- the name is not read before it is + bound); imports inside a function body; `from __future__` and star imports. + +POPULATION AND REVISION AXIS + Identical to the compile leg by construction: `tracked`, `read_worktree`, + `read_rev` and `resolve_repo` are IMPORTED from `q169_compile_sweep` rather + than copied, so the two legs cannot drift on which files they call the tree + (Q142 closed exactly that: a duplicate scanner with a different rule list + called an independent verifier). + +EXIT CODES (the tools/ contract) + 0 no fault | 1 at least one fault | 2 could not grade + +Stdlib only; nothing here writes to the tree (`--json` writes the named file). +""" +import argparse +import ast +import json +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from q169_compile_sweep import (REV_WORKTREE, read_rev, read_worktree, # noqa: E402 + resolve_repo, shown, tracked) + +FAULT_LIMIT = 40 +SCOPE = "module-level imports and the top-level statements that execute" + + +def _imports(tree): + """{name: (import_lineno, import_end_lineno)} for module-level imports. + + Walks the module body but never into a `def`/`class`/`lambda` body: an + import inside a function binds nothing at module scope, so it cannot be + the import an earlier top-level statement was counting on. + """ + found = {} + stack = list(tree.body) + while stack: + node = stack.pop() + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, + ast.Lambda)): + continue + if isinstance(node, (ast.Import, ast.ImportFrom)): + mod = getattr(node, "module", None) or "" + for alias in node.names: + if alias.name == "*": + continue + name = alias.asname or alias.name.split(".")[0] + # EARLIEST binding, not the first one visited: the walk is a + # stack, so visit order is not document order, and a name + # imported twice (pathlib at line 5 and again at line 12) was + # reported against the LATER line -- a fault for a file that + # binds the name before it reads it. + cand = (node.lineno, node.end_lineno or node.lineno, mod) + if name not in found or cand[0] < found[name][0]: + found[name] = cand + continue + stack.extend(ast.iter_child_nodes(node)) + return found + + +def _executed_reads(tree): + """{name: first_lineno} for names READ by a top-level statement that runs. + + "Runs" means: not inside a `def`/`class`/`lambda` body. A `def`'s own + decorators, defaults and annotations DO run where the `def` sits, so those + are collected separately by `_definition_time_reads`. + """ + first = {} + + def note(name, lineno): + if lineno and lineno < first.get(name, 10 ** 9): + first[name] = lineno + + def walk(node): + for child in ast.iter_child_nodes(node): + if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, + ast.ClassDef)): + # decorators, defaults and annotations execute here; the body + # does not. `walk_definition_time` collects the former. + walk_definition_time(child) + continue + if isinstance(child, ast.Lambda): + continue + if isinstance(child, ast.Name) and isinstance(child.ctx, ast.Load): + note(child.id, child.lineno) + walk(child) + + def walk_definition_time(fn): + exprs = list(getattr(fn, "decorator_list", [])) + args = getattr(fn, "args", None) + if args is not None: + exprs.extend(a for a in args.defaults if a is not None) + exprs.extend(a for a in args.kw_defaults if a is not None) + for a in exprs: + for sub in ast.walk(a): + if isinstance(sub, ast.Name) and isinstance(sub.ctx, ast.Load): + note(sub.id, sub.lineno) + + for stmt in tree.body: + if isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + walk_definition_time(stmt) + continue + if isinstance(stmt, (ast.Import, ast.ImportFrom)): + continue + if isinstance(stmt, ast.Name) and isinstance(stmt.ctx, ast.Load): + note(stmt.id, stmt.lineno) + walk(stmt) + return first + + +def _fn_reads(fn): + """{name: first_lineno} for names read anywhere inside a def/class body.""" + first = {} + for sub in ast.walk(fn): + if isinstance(sub, ast.Name) and isinstance(sub.ctx, ast.Load): + if sub.lineno < first.get(sub.id, 10 ** 9): + first[sub.id] = sub.lineno + return first + + +def order_faults(src, rel): + """[(line, why)] -- rule A, B and C, sorted by line.""" + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore", SyntaxWarning) + tree = ast.parse(src, filename=rel) + imports = _imports(tree) + if not imports: + return [] + executed = _executed_reads(tree) + faults = [] + seen = set() + + def add(line, why): + if (line, why) not in seen: + seen.add((line, why)) + faults.append((line, why)) + + # A -- read at module level before the import line + for name, (start, _end, _mod) in imports.items(): + line = executed.get(name) + if line is not None and line < start: + add(line, "%s read here, imported at line %d" % (name, start)) + + # B / C -- a def above the import that reads the name + for stmt in tree.body: + if not isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef, + ast.ClassDef)): + continue + body_reads = _fn_reads(stmt) + for name, (start, _end, _mod) in imports.items(): + if stmt.lineno >= start or name not in body_reads: + continue + called = executed.get(stmt.name) + if called is not None and called < start: + add(called, "%s read by %s() at line %d, imported at line %d" + % (name, stmt.name, called, start)) + return sorted(faults) + + +def sweep(top, rev): + """(files, faults, reason). faults: [{"path","line","why"}].""" + rels, why = tracked(top, rev) + if why: + return 0, [], why + if not rels: + return 0, [], "could not grade -- no tracked *.py at %s" % ( + "the worktree" if rev == REV_WORKTREE else rev) + faults = [] + if rev == REV_WORKTREE: + for rel in rels: + src, err = read_worktree(top, rel) + if err: + continue # q169 owns "the file is gone"; not this question + bad = order_faults(src, rel) + for line, why in bad: + faults.append({"path": rel, "line": line, "why": why}) + else: + blobs = read_rev(top, rev, rels) + for rel in rels: + src = blobs.get(rel) + if src is None: + continue + for line, why in order_faults(src, rel): + faults.append({"path": rel, "line": line, "why": why}) + return len(rels), faults, None + + +def report(rev, files, faults, quiet): + where = "the worktree" if rev == REV_WORKTREE else rev + if not quiet: + for f in faults[:FAULT_LIMIT]: + print("FAULT %s:%d %s" % (shown(f["path"]), f["line"], f["why"])) + if len(faults) > FAULT_LIMIT: + print(" ... %d more fault(s) not printed" % (len(faults) - FAULT_LIMIT)) + print("import_order: %d tracked .py at %s, %d fault(s)" + % (files, where, len(faults))) + return 1 if faults else 0 + + +# ------------------------------------------------------------------- selftest + +CLEAN = "import os\nfrom pathlib import Path\n\n\ndef f():\n return Path(os.sep)\n" +SIMPLE_BAD = "REPO = Path(__file__).resolve()\nfrom pathlib import Path\n" +EARLY_CALL = ("import sys\n\n\ndef main():\n return Path('.')\n\n\n" + "if __name__ == '__main__':\n sys.exit(main())\n" + "from pathlib import Path\n") +LATE_CALL = ("import sys\nfrom pathlib import Path\n\n\ndef main():\n" + " return Path('.')\n\n\nif __name__ == '__main__':\n" + " sys.exit(main())\n") +SYSPATH = ("import sys\nsys.path.insert(0, '/somewhere')\nimport helpers\n" + "print(helpers.x)\n") +STAR = "from os.path import *\nprint(join('a', 'b'))\n" +DECORATED = ("import functools\n\n\n@functools.wraps(Path)\ndef f():\n pass\n" + "from pathlib import Path\n") +NESTED = ("def f():\n from pathlib import Path\n return Path('.')\n") + + +def selftest(): + cells = passed = 0 + failures = [] + + def check(name, got, want): + nonlocal cells, passed + cells += 1 + if got == want: + passed += 1 + else: + failures.append("%s: got %r want %r" % (name, got, want)) + + def names(src): + return [why.split()[0] for _line, why in order_faults(src, "x.py")] + + check("1 clean file has no fault", order_faults(CLEAN, "x.py"), []) + check("2 module-level read before import", names(SIMPLE_BAD), ["Path"]) + check("2b the line is the read, not the import", + [ln for ln, _ in order_faults(SIMPLE_BAD, "x.py")], [1]) + check("2c the import line is named in the why", + "imported at line 2" in order_faults(SIMPLE_BAD, "x.py")[0][1], True) + check("3 def called before the import", names(EARLY_CALL), ["Path"]) + check("3b the call site is the line", + [ln for ln, _ in order_faults(EARLY_CALL, "x.py")], [9]) + check("4 def called after the import is silent", + order_faults(LATE_CALL, "x.py"), []) + check("5 sys.path.insert above an import is silent", + order_faults(SYSPATH, "x.py"), []) + check("6 star import is not a binding", order_faults(STAR, "x.py"), []) + check("7 a decorator reading the name is a fault", + names(DECORATED), ["Path"]) + check("8 an import inside a function binds nothing at module scope", + order_faults(NESTED, "x.py"), []) + check("9 a file with no imports has no fault", + order_faults("x = 1\nprint(x)\n", "x.py"), []) + check("10 one fault per name, not one per read", + len(order_faults("print(os)\nprint(os)\nimport os\n", "x.py")), 1) + check("11 report() is rc 1 on a fault and rc 0 clean", + (report("WORKTREE", 2, [{"path": "x.py", "line": 1, "why": "y"}], True), + report("WORKTREE", 2, [], True)), (1, 0)) + + with __import__("tempfile").TemporaryDirectory(prefix="q170-selftest-") as td: + plain = os.path.join(td, "plain") + os.makedirs(plain) + top, why = resolve_repo(plain) + check("12 non-repository refused", + (top, "not a git repository" in (why or "")), (None, True)) + rc = main(["--repo", td, "--quiet"]) + check("13 a tree with no .py is rc 2", rc, 2) + + print("selftest: %d cells, %d passed, %d failed" + % (cells, passed, len(failures))) + for f in failures: + print("FAIL %s" % f) + return 0 if passed == cells else 1 + + +# ----------------------------------------------------------------------- main + +def main(argv=None): + ap = argparse.ArgumentParser( + description="every module-level import is bound before the name it " + "binds is executed") + ap.add_argument("--repo", default=".", help="repository to grade (default .)") + ap.add_argument("--rev", default=REV_WORKTREE, + help="WORKTREE (default) or a git revision") + ap.add_argument("--json", metavar="PATH", help="write the rows here") + ap.add_argument("--quiet", action="store_true", help="summary line only") + ap.add_argument("--selftest", action="store_true", help="run the fixtures") + a = ap.parse_args(argv) + if a.selftest: + return selftest() + + top, why = resolve_repo(a.repo) + if why: + print(why, file=sys.stderr) + return 2 + files, faults, why = sweep(top, a.rev) + if why: + print(why, file=sys.stderr) + return 2 + rc = report(a.rev, files, faults, a.quiet) + if a.json: + try: + with open(a.json, "w") as fh: + json.dump({"repo": top, "rev": a.rev, "files": files, + "faults": faults, "rc": rc}, fh, indent=1) + except OSError as exc: + print("cannot write --json %s: %s" % (a.json, exc), file=sys.stderr) + return 2 + return rc + + +if __name__ == "__main__": + sys.exit(main())