diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 74aaa75..8c8be82 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -14735,3 +14735,21 @@ Files: `wizard-work/volt/q118_verify.py` (`resolve_module`, `declared_caches`, t CHECK: `bash wizard-work/volt/q69_q118hostpin_check.sh` + +**Q69 addendum 3b — the guard's `derive` leg is split, 56.1 s → 3.6 s, so the five legs fit a CHECK (volt, 2026-11-04, at the commit this paragraph lands in).** + +**Measured, not argued: the guard as landed in `93ca4a8` read rc 0 in 56.1 s, and its `derive` leg spent that time running two whole instruments end to end — every one of the seven sleep-bearing cells, twice, to read one line. The addendum above names that cost as the thing that keeps the guard out of a gate; this paragraph removes it without changing what the leg asserts.** + +**(1) What changed.** `q118_verify.py` gained `--legs` (comma-separated, deployed or mutant names; an unknown name is rc 2 listing the known ones; no flag keeps the eight-leg default byte-for-byte). `derive` now runs each subject with `--legs firstok`, which still performs a real tick before the cache digests — the leg's subject is a run, not a static read. + +**(2) The negative control moved with it.** The pre-fix comparison used to be a second end-to-end run of `6d8ddb9`'s blob. It is now a copy of the current instrument whose `declared_caches` is replaced by the two host paths the pre-fix file typed in, built in the temp dir — the same defect one frame lower, at ~1 s. `derive` reads: derived 2 of 2, typed-path control 0 of 2. + +**(3) Measured after.** Guard rc 0, 5 legs, 0 faults, **3.6 s** (was 56.1 s). The census leg is narrowed with `--lane wizard-work/volt` (13.9 s → 1.3 s); that lane spans the graded file (43 graded, 7 code files), so 0 rows is still a reading about it. `CHECK-q118` unchanged: rc 0, `q118: 8 leg(s), 8 passed, 0 fault(s)`, 23.8 s. `tools/verify.py --fast`: 12 legs, 11 pass, 0 fault, 1 skipped, rc 0. + +**(4) The guard still reds.** With `6d8ddb9`'s blob swapped in at the same path: rc 1, 4 faults — `text` 3 literals, `census` 4 rows, `refuse` rc 0, `derive` rc 2 — then the file restored (sha256 `85285d0b1dcf3bf8…` before and after). + +**(5) Not claimed.** That this wires the guard into `tools/verify.py` — it does not; it removes the cost that would have blocked that. That the pre-fix blob's rc 0 was false as a reading — every leg it ran was true of the host copy; what was false was the subject. That `--legs` grades a different retry — `firstok` is one of the deployed cells, and the full eight still gate Q118. + +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` diff --git a/docs/queue-index.md b/docs/queue-index.md index 0fd0bab..8f66cd0 100644 --- a/docs/queue-index.md +++ b/docs/queue-index.md @@ -279,7 +279,7 @@ | 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-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 … | -- | +| 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 … | -- | ## Ids with no `## ` heading @@ -316,7 +316,7 @@ Opened as records inside another section. `line` is the record's own opening lin | Q115 | 13282 | 13282 | 10 | Q114 | BULLET+BOLD | BOLD-LEAD | | Q116 | 13286 | 13286 | 12 | Q105 | BULLET+BOLD | BOLD-LEAD | | Q117 | 13290 | 13288 | 7 | Q96 | BULLET+BOLD | BOLD-LEAD | -| Q118 | 13300 | 13300 | 5 | Q111 | BULLET+BOLD | BOLD-LEAD | +| Q118 | 13300 | 13300 | 6 | Q111 | BULLET+BOLD | BOLD-LEAD | | Q119 | 13305 | 13305 | 6 | Q118 | BULLET+BOLD | BOLD-LEAD | | Q120 | 13308 | 13308 | 21 | Q119 | BULLET+BOLD | BOLD-LEAD | | Q121 | 13342 | 13342 | 4 | Q107 | BULLET+BOLD | BOLD-LEAD | @@ -6029,12 +6029,12 @@ 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": 7181, - "end_line": 14737, + "bytes": 9731, + "end_line": 14755, "id": "Q69", "kind": "heading", "line": 14716, - "lines": 22, + "lines": 40, "role": "continuation", "role_detail": "addendum", "status": null, @@ -6309,7 +6309,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q118", "line": 13300, - "refs": 5, + "refs": 6, "section": "Q111", "shape": "BULLET+BOLD" }, @@ -6589,9 +6589,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 2564423, - "queue_lines": 14737, - "queue_sha256": "83792ee6e2beecbbec1377d5e7828e2e293f82a055eaa2d3d9a1b740225427eb", + "queue_bytes": 2566973, + "queue_lines": 14755, + "queue_sha256": "88c33c659e36ef072a99743ee93a884d2ffb523a3d7031b2666bcaf7f3f952e6", "sections": [ { "bytes": 3139, @@ -8141,12 +8141,12 @@ 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": 7181, - "end_line": 14737, + "bytes": 9731, + "end_line": 14755, "id": "Q69", "kind": "heading", "line": 14716, - "lines": 22, + "lines": 40, "role": "continuation", "role_detail": "addendum", "status": null, diff --git a/wizard-work/volt/q118_verify.py b/wizard-work/volt/q118_verify.py index 5315a07..6e5f9bf 100644 --- a/wizard-work/volt/q118_verify.py +++ b/wizard-work/volt/q118_verify.py @@ -65,9 +65,18 @@ WHERE THE SUBJECT COMES FROM -- and why no path is typed into this file Run: python3 wizard-work/volt/q118_verify.py Q118_MONITOR= python3 wizard-work/volt/q118_verify.py + python3 wizard-work/volt/q118_verify.py --legs firstok 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`). + +`--legs` narrows the run to the named cells (comma-separated, deployed or +mutant). It exists for callers that grade one cell many times -- the Q69 guard +checks which paths the host-cache leg names, and running all seven sleep-bearing +cells to read one line cost it ~45 s per subject. A narrowed run still performs +a real tick before the cache digests, so `host-cache` keeps its subject; an +unknown name is rc 2, never a silent full run. With no `--legs` the eight-leg +default is unchanged. """ import hashlib import json @@ -167,14 +176,51 @@ def digest(path): return "absent" -def resolve_module(argv): - """Which file to grade: argv[1], then $Q118_MONITOR, then nothing. +def parse_args(argv): + """Split the subject (`argv[1]`), the optional `--legs` list, and an error. + + An error string means rc 2 before anything is graded: a selector that names + no known cell must not fall back to a full run, or the caller reads a green + about cells it did not ask for. + """ + module, legs = None, None + rest = argv[1:] + i = 0 + while i < len(rest): + arg = rest[i] + if arg == "--legs": + if i + 1 >= len(rest): + return None, None, "--legs needs a value" + legs, i = rest[i + 1], i + 2 + continue + if arg.startswith("--legs="): + legs, i = arg.split("=", 1)[1], i + 1 + continue + if arg.startswith("-") and arg != "-": + return None, None, f"unknown flag {arg!r}" + if module is not None: + return None, None, f"two subjects named ({module}, {arg})" + module, i = Path(arg), i + 1 + if legs is None: + return module, None, None + known = [n for n, _ in DEPLOYED_CASES] + [m for m, _, _ in MUTANT_CASES] + picked = [x.strip() for x in legs.split(",") if x.strip()] + bad = [x for x in picked if x not in known] + if bad: + return None, None, f"unknown leg(s) {bad}; known: {known}" + if not picked: + return None, None, "--legs named no leg" + return module, picked, None + + +def resolve_module(module): + """Which file to grade: the positional subject, then $Q118_MONITOR. 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]" + if module is not None: + return module, "argv[1]" env = os.environ.get(MODULE_ENV, "").strip() if env: return Path(env), MODULE_ENV @@ -375,13 +421,21 @@ MUTANT_CASES = ( def main() -> int: global MODULE - module, source = resolve_module(sys.argv) + subject, legs, err = parse_args(sys.argv) + if err: + print(f"q118: could not grade -- {err}", file=sys.stderr) + return 2 + module, source = resolve_module(subject) 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 MODULE = module + if legs: + print(f"q118: narrowed to {', '.join(legs)}") + deployed = [(n, f) for n, f in DEPLOYED_CASES if legs is None or n in legs] + mutants = [(m, t, f) for m, t, f in MUTANT_CASES if legs is None or m in legs] if not MODULE.is_file(): print(f"FAIL: could not grade -- module missing at {MODULE} (named by {source})", file=sys.stderr) @@ -395,12 +449,12 @@ def main() -> int: before = {str(p): digest(p) for p in caches} passed = 0 try: - for name, fn in DEPLOYED_CASES: + for name, fn in deployed: good, detail = fn() (ok if good else fault)(name, detail) passed += 1 if good else 0 - for mutation, target, fn in MUTANT_CASES: + for mutation, target, fn in mutants: good, detail = fn(mutate=mutation) if good: fault(f"mutant:{mutation}", f"the {target} cell stayed green -- it does " @@ -419,7 +473,7 @@ def main() -> int: ok("host-cache", f"declared caches unchanged after the run: {named}") passed += 1 - total = len(DEPLOYED_CASES) + len(MUTANT_CASES) + 1 + total = len(deployed) + len(mutants) + 1 print(f"q118: {total} leg(s), {passed} passed, {len(FAULTS)} fault(s)") return 0 if not FAULTS else 1 diff --git a/wizard-work/volt/q69_q118hostpin_check.sh b/wizard-work/volt/q69_q118hostpin_check.sh index 8ed38e9..78b012c 100755 --- a/wizard-work/volt/q69_q118hostpin_check.sh +++ b/wizard-work/volt/q69_q118hostpin_check.sh @@ -10,18 +10,28 @@ # 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) +# census the AST census lists no row for the instrument (rows are mentions); +# narrowed with --lane to this lane, which still spans the graded file, +# so the leg costs ~1 s instead of ~14 s over all 1366 graded files # 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) +# derive a copy with renamed caches is graded at ITS names, and a copy of the +# instrument that types the pair back in (the pre-fix behaviour) reads +# 0 of that copy's 2 paths -- the negative control, because a leg that +# cannot name a wrong pair cannot be said to name the right one # 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. +# +# Wall time: ~6 s. `derive` used to run two whole instruments end to end -- all +# seven sleep-bearing cells, ~23 s each, ~56 s for the guard -- to read one line. +# It now runs each subject with `q118_verify.py --legs firstok`, which still +# performs a real tick before the cache digests, so the leg's subject is +# unchanged and it pays only the tick it reads. 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 +LANE="wizard-work/volt" faults=0 leg() { printf '%-7s %s\n' "$1" "$2"; } @@ -32,10 +42,6 @@ for f in "$ROOT/$SUB" "$ROOT/$CENSUS"; do 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 @@ -49,7 +55,7 @@ else 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 +if python3 "$ROOT/$CENSUS" --lane "$LANE" --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)" @@ -70,50 +76,63 @@ else 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 +# derive -- the host-cache leg follows the module's own declaration, and a copy +# of the instrument that types the pair instead reads 0 of the copy's 2 paths +mkdir -p "$TMP/copy" +python3 - "$ROOT/$SUB" "$TMP/copy/troupe_pipe_monitor.py" "$TMP/typed118.py" \ + "$TMP/copy" <<'PY' >"$TMP/build.out" 2>&1 +import re +import sys from pathlib import Path + +inst, copy_mod, typed_inst = (Path(a) for a in sys.argv[1:4]) +copydir = Path(sys.argv[4]) 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) +for pat, name, leaf in ((r"^CACHE = .*$", "CACHE", "attrib-copy.json"), + (r"^LISTING_CACHE = .*$", "LISTING_CACHE", "listing-copy.json")): + text, n = re.subn(pat, '%s = Path("%s")' % (name, copydir / leaf), + 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") +copy_mod.write_text(text, encoding="utf-8") + +body = inst.read_text(encoding="utf-8") +anchor = '\nif __name__ == "__main__":' +if body.count(anchor) != 1: + raise SystemExit("main-guard anchor matched %d time(s)" % body.count(anchor)) +typed = ( + '\ndef declared_caches(module):\n' + ' """Negative control: the pre-fix behaviour, the pair typed in here."""\n' + ' return (Path("/root/.hermes/cache/troupe_last_touch.json"),\n' + ' Path("/root/.hermes/cache/troupe_folder_listing.json"))\n' +) +typed_inst.write_text(body.replace(anchor, typed + anchor), encoding="utf-8") +print("built copy and typed-path control") 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)) +buildrc=$? +if [ "$buildrc" -ne 0 ] || [ ! -f "$TMP/copy/troupe_pipe_monitor.py" ] \ + || [ ! -f "$TMP/typed118.py" ]; then + leg derive "FAULT (could not build the copy: $(tail -1 "$TMP/build.out"))" + faults=$((faults + 1)) +else + python3 "$ROOT/$SUB" --legs firstok "$TMP/copy/troupe_pipe_monitor.py" \ + >"$TMP/new.out" 2>&1 + newrc=$? + python3 "$TMP/typed118.py" --legs firstok "$TMP/copy/troupe_pipe_monitor.py" \ + >"$TMP/typed.out" 2>&1 + typerc=$? + 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")" + typedspoke="$(grep -c '^ok host-cache -- declared caches unchanged after the run: /root/.hermes/cache/troupe_last_touch.json, /root/.hermes/cache/troupe_folder_listing.json$' "$TMP/typed.out")" + typedcopy="$(grep -cF -- "$TMP/copy/" "$TMP/typed.out")" + if [ "$newrc" -eq 0 ] && [ "$newhas" -eq 1 ] && [ "$typerc" -eq 0 ] \ + && [ "$typedspoke" -eq 1 ] && [ "$typedcopy" -eq 0 ]; then + leg derive "ok (copy's 2 paths: derived 2 of 2, typed-path control 0 of 2)" 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 + leg derive "FAULT (new rc $newrc names copy $newhas; typed control rc $typerc spoke $typedspoke named copy $typedcopy)" + faults=$((faults + 1)) fi fi