diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 52453e2..74aaa75 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -14712,3 +14712,26 @@ At `406dd17`, `python3 tools/verify.py --fast` read **12 legs, 10 pass, 1 fault, Files: `tools/queue_append_audit.py` (the pin axis, `grade_index`/`grade_pin`, `--axis`, the report columns, the 43-cell selftest), `CHECK-append-pin` (new, five legs), `wizard-work/volt/q137-open-set/open_set.py` (the `PIN_COUNTS` move), `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (one row), `WORK-QUEUE.md` (this record), `docs/queue-index.md` (re-rendered). Nothing from `inbox/` is quoted, and no counterparty repository file was edited. CHECK: `bash CHECK-append-pin` + +## Q69 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, 2026-11-04, at `6d8ddb9` and the commit this record lands in; Q69's own "one file per commit" clause)* + +**Q69 stays open. This record repairs one of the eight files the second addendum named and takes no other; no id changes state and no other lane's file is touched.** + +**(1) The defect, re-read at `6d8ddb9`.** `wizard-work/volt/q118_verify.py` carried three host-absolute paths: `DEFAULT_MODULE = "/root/.hermes/scripts/troupe_pipe_monitor.py"` (the monitor is not in this repository -- the cron job runs it by host path) and `HOST_CACHES` as the two deployed cache files. The census listed exactly those three: `{"line": 64, "subtype": "CONST"}`, `{"line": 67, "subtype": "READ"}`, `{"line": 68, "subtype": "READ"}`. Two behaviours followed, and both are measured rather than argued. **First**, whoever ran the tool with no argument graded one host's copy from any checkout: HEAD's blob, run with no argument, reads **rc 0, `q118: 8 leg(s), 8 passed, 0 fault(s)`** -- a green about a file that may not be the tree the tool was read out of. **Second**, the host-cache leg's subject was the pair typed into the file, not the pair the graded module declares, so the documented use of the argument (point a copy at the tool) graded the copy's *code* and the host's *caches*: over a copy whose two cache paths were renamed, HEAD's blob read `ok host-cache -- deployed caches byte-identical after the run` and named **0 of the copy's 2 paths**. + +**(2) The repair: the subject is named by the caller, and the caches are read out of the subject.** `resolve_module` takes `argv[1]`, else `$Q118_MONITOR`, else nothing -- and nothing is a refusal (rc 2) that says how to name one, not a default that guesses whose host to grade. `declared_caches` reads the graded module's own `CACHE = Path("...")` and `LISTING_CACHE = Path("...")` lines, through the same two anchors `build_module` already rewrites, so a renamed cache cannot drift the leg from its subject and one table now serves both the rewrite and the read. A module whose anchors do not match is rc 2 (could not grade), never a green: an instrument that cannot say which files a run must leave alone must not report on them. The docstring states the rule and the pin it moved: the host subject now lives in `CHECK-q118` alone, where host pins belong (the Q106 class). + +**(3) Measured after the edit, at this tree.** `bash wizard-work/volt/CHECK-q118` -> **rc 0**, `q118: 8 leg(s), 8 passed, 0 fault(s)`. With no argument and no `$Q118_MONITOR` -> **rc 2**, `could not grade -- no monitor named ... pass the path as argv[1] or set Q118_MONITOR.` The text predicate that the guard grades (`"(/root|/home|/var|/srv|/opt|/mnt)/`) reads **3 -> 0** between HEAD's blob and this file. The census moves by exactly the three constants and the one file: **554 -> 553 code files, 910 -> 907 constants** over 1366 graded, and the lane row reads **8 code files / 15 constants -> 7 / 12** (`wizard-work/volt 43 7 12 29 6 6`); the file keeps one prose mention, so it moves from the code population to `prose-only` (5 -> 6) rather than out of the census, which is the whole of what this repair claims to have done. + +**(4) The guard: `wizard-work/volt/q69_q118hostpin_check.sh`, five legs, rc 0/1/2.** `text` (no host-absolute string literal in the module), `census` (the JSON names the module 0 times; rows are mentions, and a clean file has none), `refuse` (no subject named is rc 2 and the message names `Q118_MONITOR`), `derive` (a copy of the monitor with both cache anchors renamed is graded at *its* names -- repaired reads 2 of 2, `6d8ddb9`'s blob reads 0 of 2 while still speaking its `deployed caches byte-identical` line) and `teeth` (the `text` predicate fires on a copy carrying one literal back). Measured here: **rc 0, 5 leg(s), 0 fault(s)**, wall time 1m3s -- the `derive` leg runs both instruments end to end, and that cost is stated rather than hidden. The three legs that grade HEAD's blob read red on it (`text` 3 literals, `census` 1 file row + 3 constant rows, `refuse` rc 0 instead of 2), so the green above is a measurement of the repair and not a tool that cannot fail. + +**(5) Bookkeeping, measured after this append and before the commit landed.** The append carries one heading (`## Q69 addendum 3`) over a lead that says Q69 stays open, so no record and no id is scored: headings 119 -> 120, heading_ids stays 103 (Q69 already carries a heading), and every record counter, `ids` and `record_only` stand still (records 131, record_ids 97, ids 158, record_only 55, open 0, opened 26); no probe is registered for a repair inside an open id, and `q69_q118hostpin_check.sh` is its guard. `PIN_COUNTS` in `open_set.py` moved to the counts this append makes, measured by `--check` after the append; `python3 tools/queue_index.py --write docs/queue-index.md` re-rendered. No row was added to `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv`: that table grades root entry points, and this guard lives under `wizard-work/volt/` beside the tool it grades. + +**(6) What stays open in this lane, re-counted.** Seven files, twelve constants: media-catalog `claims_check.py:52` and `semantic_gate.py:213` (`/root/stackchain_catalog_raw.json`); `q133_verify.py:64` and `q136_verify.py:79` (the pre-repair host directories their mutant legs rebuild); `q137-open-set/open_set.py:393,412` (the monitor); `q63_prove.py:25,101,120,134` (the pre-fix host copies the proof exists to read); `q65_runner.py:117` twice (the detector's own pattern). `q118_verify.py` is off that list by this record and nothing else moved. + +**(7) Not claimed.** That the monitor should move into this repository -- the cron job runs it by host path and that move is the pipe's decision, not this item's; what changed is that the verifier no longer asserts one host's path from a checkout. That `CHECK-q118` naming a host path is a defect of the same class -- a CHECK is where a pin belongs, and it is one path in one place, stated in its own header. That the pre-fix blob's rc 0 was a false *verdict* -- every leg it ran was true of the host copy it read; what was false was that the reading was about the tree it was run from. That a CODE row is a fault -- the census counts positions and cannot read intent. That 7/12 is stable -- it is a re-read of a tree other lanes commit to. That the guard's 1m3s is acceptable for the repo gate -- it is not wired into it, and would need the `derive` leg split before it could be. + +Files: `wizard-work/volt/q118_verify.py` (`resolve_module`, `declared_caches`, the docstring), `wizard-work/volt/CHECK-q118` (the host pin and the declared-cache assertion), `wizard-work/volt/q69_q118hostpin_check.sh` (new guard, five legs), `wizard-work/volt/q137-open-set/open_set.py` (the `PIN_COUNTS` move), `WORK-QUEUE.md` (this record), `docs/queue-index.md` (re-rendered). Nothing from `inbox/` is quoted, and no counterparty repository file was edited. + +CHECK: `bash wizard-work/volt/q69_q118hostpin_check.sh` + diff --git a/docs/queue-index.md b/docs/queue-index.md index 45da976..0fd0bab 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. 119 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. 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. | # | id | kind | lines | bytes | role | title | first `Status:` | |---|---|---|---|---|---|---|---| @@ -278,7 +278,8 @@ | 268 | Q167 | heading | 14677-14678 | 228 | opening | closure — the queue's pin sat two commits behind the queue, and nothing graded the pin in the commit *(volt, 2026-11-04, at `56d3853` and the commit this record lands in; sol lane by authorship … | -- | | 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-14714 | 8236 | 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 … | -- | +| 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-14737 | 7181 | 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 … | -- | ## Ids with no `## ` heading @@ -303,7 +304,7 @@ Opened as records inside another section. `line` is the record's own opening lin | Q103 | 13010 | 13010 | 18 | Q102 | BULLET+BOLD | BOLD-LEAD | | Q104 | 13103 | 13103 | 19 | Q101 | BULLET+BOLD | BOLD-LEAD | | Q105 | 13135 | 13135 | 20 | Q104 | BULLET+BOLD | BOLD-LEAD | -| Q106 | 13171 | 13171 | 18 | Q105 | BULLET+BOLD | BOLD-LEAD | +| Q106 | 13171 | 13171 | 19 | Q105 | BULLET+BOLD | BOLD-LEAD | | Q107 | 13189 | 13189 | 19 | Q106 | BULLET+BOLD | BOLD-LEAD | | Q108 | 13267 | 13263 | 3 | Q106 | BULLET+BOLD | BOLD-LEAD | | Q109 | 13270 | 13270 | 31 | Q104 | BULLET+BOLD | BOLD-LEAD | @@ -2321,12 +2322,12 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "**Q167 \u2014 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 \u2026" }, { - "bytes": 8236, - "end_line": 14714, + "bytes": 8237, + "end_line": 14715, "id": "Q168", "kind": "record", "line": 14696, - "lines": 19, + "lines": 20, "role": "continuation", "role_detail": "OPENED", "status": null, @@ -2358,7 +2359,8 @@ The block below is the parse this document renders, byte for byte. `--check` com "Q69": [ 11457, 14633, - 14655 + 14655, + 14716 ], "Q72": [ 11526, @@ -6014,17 +6016,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": 8236, - "end_line": 14714, + "bytes": 8237, + "end_line": 14715, "id": "Q168", "kind": "record", "line": 14696, - "lines": 19, + "lines": 20, "role": "continuation", "role_detail": "OPENED", "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": 7181, + "end_line": 14737, + "id": "Q69", + "kind": "heading", + "line": 14716, + "lines": 22, + "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" } ], "nested": [ @@ -6186,7 +6201,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q106", "line": 13171, - "refs": 18, + "refs": 19, "section": "Q105", "shape": "BULLET+BOLD" }, @@ -6574,9 +6589,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 2557241, - "queue_lines": 14714, - "queue_sha256": "9341e983b3aecc12c5f42dbd4b9df8274d6115dea08df01a29c1875bfaa7a5d7", + "queue_bytes": 2564423, + "queue_lines": 14737, + "queue_sha256": "83792ee6e2beecbbec1377d5e7828e2e293f82a055eaa2d3d9a1b740225427eb", "sections": [ { "bytes": 3139, @@ -8124,6 +8139,19 @@ The block below is the parse this document renders, byte for byte. `--check` com "status": null, "status_line": null, "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": 7181, + "end_line": 14737, + "id": "Q69", + "kind": "heading", + "line": 14716, + "lines": 22, + "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" } ] } diff --git a/wizard-work/volt/CHECK-q118 b/wizard-work/volt/CHECK-q118 index 2901c55..1072090 100755 --- a/wizard-work/volt/CHECK-q118 +++ b/wizard-work/volt/CHECK-q118 @@ -5,21 +5,32 @@ # # The artifact under repair is NOT in this repository -- the pipe's monitor lives # at /root/.hermes/scripts/troupe_pipe_monitor.py because the cron job runs it by -# host path -- so like CHECK-q115 this gate reads a host path. That is stated, -# not hidden (the Q106 host-pin class); moving the monitor into the tree is the -# pipe's decision, not this item's. Everything the gate grades is the deployed -# retry loop itself: the fake `curl` sits at the module's real boundary, so no -# function under test is stubbed. +# host path -- so unlike every other volt gate this one names a host path. That +# is stated, not hidden (the Q106 host-pin class); moving the monitor into the +# tree is the pipe's decision, not this item's. Everything the gate grades is the +# deployed retry loop itself: the fake `curl` sits at the module's real boundary, +# so no function under test is stubbed. +# +# Since the Q69 repair this file is the only place the pin lives: q118_verify.py +# carries no default module and no typed cache paths, it takes argv[1] or +# $Q118_MONITOR and refuses with rc 2 when neither names one. Override the +# subject with Q118_MONITOR=; a host without the monitor still reads rc 2 +# (could not grade), never a green. set -u HERE="$(cd "$(dirname "$0")" && pwd)" VERIFY="$HERE/q118_verify.py" +MONITOR="${Q118_MONITOR:-/root/.hermes/scripts/troupe_pipe_monitor.py}" if [ ! -f "$VERIFY" ]; then echo "FAIL: verifier missing at $VERIFY" >&2 exit 2 fi +if [ ! -f "$MONITOR" ]; then + echo "FAIL: could not grade -- monitor missing at $MONITOR (override with Q118_MONITOR)" >&2 + exit 2 +fi -out="$(python3 "$VERIFY" 2>&1)" +out="$(Q118_MONITOR="$MONITOR" python3 "$VERIFY" 2>&1)" rc=$? printf '%s\n' "$out" | grep -E '^FAIL:|^q118:' || true @@ -35,5 +46,11 @@ if ! printf '%s\n' "$out" | grep -q '^q118: 8 leg(s), 8 passed, 0 fault(s)$'; th echo "FAIL: expected 8/8 legs, got: $(printf '%s\n' "$out" | tail -1)" >&2 exit 1 fi +# the host-cache leg must speak about the subject's own declared caches, which the +# instrument now reads out of the module instead of typing into itself +if ! printf '%s\n' "$out" | grep -q '^ok host-cache -- declared caches unchanged after the run: [^,]*, '; then + echo "FAIL: the host-cache leg did not name two declared caches" >&2 + exit 1 +fi echo "q118: retry absorbs a transient read, names attempts= and last=, wakes on exhaustion -- OK" exit 0 diff --git a/wizard-work/volt/q118_verify.py b/wizard-work/volt/q118_verify.py index ef605da..5315a07 100644 --- a/wizard-work/volt/q118_verify.py +++ b/wizard-work/volt/q118_verify.py @@ -46,10 +46,28 @@ Exit codes follow the repository's tools: 0 clean, 1 graded fault, 2 could not grade (module or interpreter absent, or a constant anchor that no longer matches -- a rewrite that silently did nothing would grade the live host). -Run: python3 wizard-work/volt/q118_verify.py [module.py] -The optional argument points the instrument at a copy of the module -- that is -how a whole-artifact negative control is taken (a copy whose default retry count -is 1 must red `recover` and `exhaust`). +WHERE THE SUBJECT COMES FROM -- and why no path is typed into this file + The monitor is not in this repository (the cron job runs it by absolute host + path), so this instrument used to carry that host path as `DEFAULT_MODULE` and + the two deployed cache paths as `HOST_CACHES`. Both + were literals: the file graded one host's copy from any checkout, and the + host-cache leg spoke about two paths that were fixed in this file rather than + read from the module it was pointed at -- so a copy (the documented use of + argv[1]) or a deployment that renamed its caches was watched at the old names + and its own caches were never graded (measured: pointed at a copy whose two + caches were renamed, this file's leg read `deployed caches byte-identical` + and named 0 of the copy's 2 paths). The subject is now named by the caller -- + argv[1], else `Q118_MONITOR` -- and the cache paths are read out of the module + under grade, on the same two anchors `build_module` already rewrites. With + neither argv[1] nor `Q118_MONITOR` the instrument refuses with rc 2 instead of + guessing whose host to grade (Q69's rule for volt's tools; the host pin itself + is stated in `CHECK-q118`, where a host subject belongs). + +Run: python3 wizard-work/volt/q118_verify.py + Q118_MONITOR= python3 wizard-work/volt/q118_verify.py +The argument points the instrument at the module or a copy of it -- that is how +a whole-artifact negative control is taken (a copy whose default retry count is +1 must red `recover` and `exhaust`). """ import hashlib import json @@ -61,11 +79,14 @@ import tempfile import time from pathlib import Path -DEFAULT_MODULE = "/root/.hermes/scripts/troupe_pipe_monitor.py" -MODULE = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(DEFAULT_MODULE) -HOST_CACHES = ( - Path("/root/.hermes/cache/troupe_last_touch.json"), - Path("/root/.hermes/cache/troupe_folder_listing.json"), +DEFAULT_MODULE = None # resolved per run: argv[1], then $Q118_MONITOR; else refuse +MODULE = None +MODULE_ENV = "Q118_MONITOR" +# the two anchors build_module rewrites, reused to READ the caches the graded +# module declares -- one table, so a rename cannot drift the leg from its subject +CACHE_ANCHORS = ( + ("CACHE", r'^CACHE = Path\("([^"]+)"\)$'), + ("LISTING_CACHE", r'^LISTING_CACHE = Path\("([^"]+)"\)$'), ) ITEMS = [ {"type": "file", "path": "replies/2026-10-28-to-zephyrus-1.md", "sha": "aaa111bbb222"}, @@ -146,8 +167,41 @@ def digest(path): return "absent" +def resolve_module(argv): + """Which file to grade: argv[1], then $Q118_MONITOR, then nothing. + + Returning None is the refusal path: the monitor is outside this repository, + so any built-in default would be one host's copy of it (Q69). + """ + if len(argv) > 1: + return Path(argv[1]), "argv[1]" + env = os.environ.get(MODULE_ENV, "").strip() + if env: + return Path(env), MODULE_ENV + return None, "" + + +def declared_caches(module): + """The cache paths the graded module declares -- read, never re-typed. + + A RuntimeError here is rc 2, not a guess: an anchor that no longer matches + means this instrument cannot say which files the run must leave alone. + """ + text = module.read_text(encoding="utf-8") + out = [] + for name, anchor in CACHE_ANCHORS: + m = re.search(anchor, text, re.M) + if not m: + raise RuntimeError( + f"no `{name} = Path(\"…\")` line in {module} -- cannot say which cache " + f"the graded run must leave alone") + out.append(Path(m.group(1))) + return tuple(out) + + def build_module(work: Path, mutate=None) -> Path: """Copy the deployed module with its four outside-world constants repointed.""" + assert MODULE is not None, "build_module needs a module resolved by main()" text = MODULE.read_text(encoding="utf-8") if mutate: old, new = MUTATIONS[mutate] @@ -320,10 +374,25 @@ MUTANT_CASES = ( def main() -> int: - if not MODULE.is_file(): - print(f"FAIL: could not grade -- module missing at {MODULE}", file=sys.stderr) + global MODULE + module, source = resolve_module(sys.argv) + if module is None: + print(f"q118: could not grade -- no monitor named. The monitor is not in this " + f"repository, so a built-in default would be one host's copy; pass the " + f"path as argv[1] or set {MODULE_ENV}.", file=sys.stderr) return 2 - before = {str(p): digest(p) for p in HOST_CACHES} + MODULE = module + if not MODULE.is_file(): + print(f"FAIL: could not grade -- module missing at {MODULE} (named by {source})", + file=sys.stderr) + return 2 + try: + caches = declared_caches(MODULE) + except (RuntimeError, OSError) as exc: + print(f"FAIL: could not grade -- {exc}", file=sys.stderr) + return 2 + named = ", ".join(str(p) for p in caches) + before = {str(p): digest(p) for p in caches} passed = 0 try: for name, fn in DEPLOYED_CASES: @@ -343,11 +412,11 @@ def main() -> int: print(f"FAIL: could not grade -- {exc}", file=sys.stderr) return 2 - after = {str(p): digest(p) for p in HOST_CACHES} + after = {str(p): digest(p) for p in caches} if before != after: - fault("host-cache", f"a graded run wrote host state: {before} -> {after}") + fault("host-cache", f"a graded run wrote the module's declared cache: {before} -> {after}") else: - ok("host-cache", "deployed caches byte-identical after the run") + ok("host-cache", f"declared caches unchanged after the run: {named}") passed += 1 total = len(DEPLOYED_CASES) + len(MUTANT_CASES) + 1 diff --git a/wizard-work/volt/q137-open-set/open_set.py b/wizard-work/volt/q137-open-set/open_set.py index 868cc4e..da22d42 100644 --- a/wizard-work/volt/q137-open-set/open_set.py +++ b/wizard-work/volt/q137-open-set/open_set.py @@ -647,7 +647,14 @@ PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109, # append, which named exactly these five faults and no others, and the audit # that repairs this item now grades this pin as a second axis in the commit -- # this record's own commit included. -PIN_COUNTS = {"headings": 119, "heading_ids": 103, "records": 131, +# Q69 addendum 3 (2026-11-04): the same shape as addendum 2 -- a section heading +# (## Q69 addendum 3 ...) over a lead that says Q69 stays open, so no record and +# no id is scored: headings 119 -> 120 with heading_ids staying 103 (Q69 already +# carries a heading), and every record counter, ids and record_only unchanged: +# 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, "record_only": 55} diff --git a/wizard-work/volt/q69_q118hostpin_check.sh b/wizard-work/volt/q69_q118hostpin_check.sh new file mode 100755 index 0000000..8ed38e9 --- /dev/null +++ b/wizard-work/volt/q69_q118hostpin_check.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# CHECK -- the Q69 repair of wizard-work/volt/q118_verify.py's host pin. +# +# The instrument hard-coded three host-absolute paths: the deployed monitor as +# DEFAULT_MODULE, and the deployed pair of caches as HOST_CACHES. The Q69 defect +# is the first one (whoever ran the tool without an argument graded one host's +# copy from any checkout); the second is the same defect one level down -- the +# host-cache leg spoke about two paths typed into this file rather than read out +# of the module it was pointed at, so a copy (the documented use of argv[1]) or +# a deployment that renamed its caches was watched at the old names and its own +# caches were never graded. Five legs, each falsifiable on its own: +# text the instrument carries no host-absolute string literal +# census the AST census lists no row for the instrument (rows are mentions) +# refuse with no argv[1] and no $Q118_MONITOR it refuses rc 2 and says how +# derive a copy with renamed caches is graded at ITS names; the pre-fix blob +# over the same copy is graded at the host names it carries (0 of 2) +# teeth the text leg's predicate fires on a copy carrying the literal back +# rc 0 clean, rc 1 a fault it graded, rc 2 it could not grade at all. +set -u + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SUB="wizard-work/volt/q118_verify.py" +CENSUS="wizard-work/volt/q69_hostroot_census.py" +PRE_FIX_REV=6d8ddb9 # the commit this repair is measured against + +faults=0 +leg() { printf '%-7s %s\n' "$1" "$2"; } + +for f in "$ROOT/$SUB" "$ROOT/$CENSUS"; do + if [ ! -f "$f" ]; then + echo "q69-q118: missing $f; cannot grade" >&2 + exit 2 + fi +done +if ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then + echo "q69-q118: $ROOT is not a git repository; the pre-fix leg cannot read it" >&2 + exit 2 +fi + +TMP="$(mktemp -d "${TMPDIR:-/tmp}/q69-q118.XXXXXX")" || exit 2 +trap 'rm -rf "$TMP"' EXIT + +# text -- no host-absolute literal survives in the instrument +hits="$(grep -c '"/\(root\|home\|var\|srv\|opt\|mnt\)/' "$ROOT/$SUB")" +if [ "$hits" -eq 0 ]; then + leg text "ok (0 host-absolute literal)" +else + leg text "FAULT ($hits host-absolute literal)"; faults=$((faults + 1)) +fi + +# census -- the AST census mentions the instrument not at all +if python3 "$ROOT/$CENSUS" --json "$TMP/census.json" >"$TMP/census.out" 2>&1; then + rows="$(grep -c 'q118_verify.py' "$TMP/census.json")" + if [ "$rows" -eq 0 ]; then + leg census "ok (0 row for $SUB)" + else + leg census "FAULT ($rows row(s) name the instrument)"; faults=$((faults + 1)) + fi +else + leg census "FAULT (census rc $? -- $(tail -1 "$TMP/census.out"))" + faults=$((faults + 1)) +fi + +# refuse -- no subject named is rc 2 with the way to name one, never a default +rout="$(env -u Q118_MONITOR python3 "$ROOT/$SUB" 2>&1)"; rrc=$? +if [ "$rrc" -eq 2 ] && printf '%s\n' "$rout" | grep -q 'Q118_MONITOR'; then + leg refuse "ok (rc 2, message names Q118_MONITOR)" +else + leg refuse "FAULT (rc $rrc, message: $(printf '%s\n' "$rout" | tail -1))" + faults=$((faults + 1)) +fi + +# derive -- the host-cache leg follows the module's own declaration +git -C "$ROOT" show "$PRE_FIX_REV:$SUB" >"$TMP/pre118.py" 2>"$TMP/pre.err" +if [ ! -s "$TMP/pre118.py" ]; then + leg derive "FAULT (no pre-fix blob at $PRE_FIX_REV:$SUB -- $(tail -1 "$TMP/pre.err"))" + faults=$((faults + 1)) +else + mkdir -p "$TMP/copy" + python3 - "$TMP/copy/troupe_pipe_monitor.py" <<'PY' >"$TMP/copy.out" 2>&1 +import re, sys +from pathlib import Path +src = Path("/root/.hermes/scripts/troupe_pipe_monitor.py") +if not src.is_file(): + raise SystemExit("no deployed module to copy") +dest = Path(sys.argv[1]) +text = src.read_text(encoding="utf-8") +for pat, rep in ((r"^CACHE = .*$", 'CACHE = Path("%s")' % (dest.parent / "attrib-copy.json")), + (r"^LISTING_CACHE = .*$", 'LISTING_CACHE = Path("%s")' % (dest.parent / "listing-copy.json"))): + text, n = re.subn(pat, rep, text, count=1, flags=re.M) + if n != 1: + raise SystemExit("anchor %r matched %d time(s)" % (pat, n)) +dest.write_text(text, encoding="utf-8") +PY + copyrc=$? + if [ "$copyrc" -ne 0 ] || [ ! -f "$TMP/copy/troupe_pipe_monitor.py" ]; then + leg derive "FAULT (could not build the copy: $(tail -1 "$TMP/copy.out"))" + faults=$((faults + 1)) + else + python3 "$ROOT/$SUB" "$TMP/copy/troupe_pipe_monitor.py" >"$TMP/new.out" 2>&1 + newrc=$? + python3 "$TMP/pre118.py" "$TMP/copy/troupe_pipe_monitor.py" >"$TMP/old.out" 2>&1 + oldrc=$? + newhas="$(grep -c "^ok host-cache -- declared caches unchanged after the run: $TMP/copy/attrib-copy.json, $TMP/copy/listing-copy.json$" "$TMP/new.out")" + oldspoke="$(grep -c '^ok host-cache -- deployed caches byte-identical after the run$' "$TMP/old.out")" + oldhas="$(grep -c "$TMP/copy/attrib-copy.json" "$TMP/old.out")" + # the pre-fix blob's own rc is not graded here: its timing legs (firstok + # asserts a tick under 1.5 s wall) read rc 1 once under load in this + # clone and rc 0 on two immediate re-runs. What is graded is that its + # host-cache leg SPOKE (about the host pair) and named the copy 0 times. + if [ "$newrc" -eq 0 ] && [ "$newhas" -eq 1 ] && [ "$oldspoke" -eq 1 ] && [ "$oldhas" -eq 0 ]; then + leg derive "ok (copy's 2 paths named: new 2 of 2, pre-fix 0 of 2)" + else + leg derive "FAULT (new rc $newrc names copy: $newhas; pre-fix rc $oldrc spoke: $oldspoke names copy: $oldhas)" + faults=$((faults + 1)) + fi + fi +fi + +# teeth -- the text leg's predicate is not one that cannot fire +cp "$ROOT/$SUB" "$TMP/pinned.py" || exit 2 +printf 'DEFAULT_MODULE = "/root/.hermes/scripts/troupe_pipe_monitor.py"\n' >>"$TMP/pinned.py" +poison="$(grep -c '"/\(root\|home\|var\|srv\|opt\|mnt\)/' "$TMP/pinned.py")" +if [ "$poison" -ge 1 ]; then + leg teeth "ok (poisoned copy reads $poison, clean copy reads 0)" +else + leg teeth "FAULT (poisoned copy reads 0 -- the text leg cannot fail)" + faults=$((faults + 1)) +fi + +if [ "$faults" -eq 0 ]; then + echo "q69-q118: 5 leg(s), 0 fault(s)" + exit 0 +fi +echo "q69-q118: 5 leg(s), $faults fault(s)" +exit 1