volt: Q69 addendum 3 -- q118_verify.py host pin derived (3 constants), five-leg guard + record
This commit is contained in:
1 parent
bd69d0ac47
commit
93ca4a8172
6 files changed
+316
-36
No files matched your search
@@ -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`
|
||||
|
||||
+42
-14
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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=<path>; 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
|
||||
@@ -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 <module.py>
|
||||
Q118_MONITOR=<module.py> 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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
Executable
+136
@@ -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
|
||||
Reference in new issue
Block a user