diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 2081fcd..f730220 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -10536,3 +10536,19 @@ Verification, read at `6cd2114` (the revision this record was written against; a 7. **What it does not claim.** The reply-count guard is sentinel's artifact outside this repo and still hardcodes `249` against an archive of 257 — I did not edit another lane's file, and it was red before this tick, independent of the restore; it stays red until its snapshot advances, which is the point of point 2. The restored reply is byte-identical to `bf1b792`'s blob, but beam's commits were not re-verified this tick — restoration is not re-verification. `TARGET` closes a red only where the row's own citation holds, and the census still runs over a corpus of 499 files, not the archive's prose. No Q-item state changed, and nothing here decides anything Alexander holds. — Volt + +**Pre-commit guard 2026-10-21 (volt, Workhorse) — the map's drift guard graded a revision, so the ten minutes before a commit were ungraded: `--rev WORKTREE` derives the map from the files git would commit, and it was measured separating the two trees on a real worktree, not only on a fixture.** No item opened or closed; the open set is as the previous record left it (`Q37`'s pipe span, the career-lane `Q53` decision held for Alexander, `Q64`'s design question Alexander's). Appended at the foot; `docs/queue-index.md` re-rendered in the same commit. + +1. **The blind spot, measured at `4602bd5` from inside a detached worktree.** That commit's message states "TOOLS.md re-rendered, byte-identical"; the map it ships is not the render of its own tree. `python3 tools/tool_map.py --repo . --check docs/TOOLS.md` reads **rc 1, `DRIFT at line 49`** — the `write-axis` row, whose description moved in that commit's `verify.py` and whose document was not re-rendered. `--repo . --selftest` reads **15/16, rc 1**, the failure being the currency cell. Before the commit existed, the same commands on the pre-commit worktree (`HEAD` = `ea8fe24`, the edit only on disk) read **rc 0** and **16/16**: the guard was working, and it was guarding the last commit instead of the one about to be made. + +2. **Why the gate stayed green, and the claim that did not survive re-measurement.** `selftest_sweep.py --repo .` at `4602bd5` reads **21 tools, 21 OK, 0 fatal**, measured today. `selftest_sweep.py:449` runs each tool as `[python, path, "--selftest"]` with **no `--repo`**, and `tool_map.py`'s `DEFAULT_REPO` is an absolute path, so its selftest graded `/root/troupe-standup` — the clean clone — from inside the drifted worktree. The first draft of this tick's docstring claimed the sweep leg went fatal at 17:28:26Z and stayed fatal for two hours; that reading did not reproduce, and the sentence was **deleted and replaced by the measurement above**, not softened. The sweep's wrong-tree axis is a second defect and is **not fixed here**. + +3. **What landed** in `tools/tool_map.py`: a `WORKTREE` sentinel accepted by the existing `--rev`; `read_blob()` reads the file on disk for it; `tracked_tools()` discovers through `git ls-files -- tools/` (the index is what a commit of tracked files carries) and leaves `HEAD` on `git ls-tree`; `main()` skips `rev-parse --verify` only for the sentinel; `graderc()` passes a rev so the new cells exercise the parser. Four cells were added: a clean worktree renders exactly what `HEAD` renders; the map reads the file on disk, not the blob at `HEAD`; one uncommitted docstring edit is **rc 0 at HEAD and rc 1 at WORKTREE**; `WORKTREE` outside a git work tree is **rc 2**, not a crash. **Selftest 16 → 20 cells, 20/20 rc 0.** + +4. **Measured outside the fixture, on a second worktree.** A clean detached worktree at `190d1aa`: `--check` **rc 0**, `--rev WORKTREE --check` **rc 0**, both against the same document. With one line appended to `verify.py`'s `write-axis` description and nothing committed: `--rev HEAD` still **rc 0**, `--rev WORKTREE` **rc 1, `DRIFT at line 49`**. Transcript: `/root/.hermes/wizard-outputs/volt/2026-10-21-worktree-mode/measure.txt`. + +5. **Verification at the commit carrying this record.** `tool_map.py --selftest` **20/20 rc 0**; `--repo . --check docs/TOOLS.md` **rc 0**; `--repo . --rev WORKTREE --check docs/TOOLS.md` **rc 0** (no derived cell moved, so the document is unchanged); `verify.py --repo .` **6 legs, 6 pass, 0 fault, 0 ungradable, 0 skipped**; `doc_command_check.py --repo . --fail` **65 commands over 19 oracles, OK 65** (one more command than the previous tick: this record's usage line); `doc_census_check.py --repo . --tools tools/tool_map.py --fail` **0 INTERNAL SPLIT, 0 STALE**. + +6. **What it does not claim.** `WORKTREE` is a literal name, so a branch of that name is shadowed by the sentinel. The mode grades tracked files: a tool that has not been `git add`ed is not a row — the same blind spot one step earlier, and why the usage line says "as it will be committed". The currency cell stays `HEAD`-scoped by design. The sweep's missing `--repo` is recorded, not repaired: another lane's instrument, and a fix there needs its own red-first evidence. No Q-item state changed, and nothing here decides anything Alexander holds. + +— Volt diff --git a/docs/queue-index.md b/docs/queue-index.md index caa0738..a5e9e2d 100644 --- a/docs/queue-index.md +++ b/docs/queue-index.md @@ -56,7 +56,7 @@ | 46 | Q44 | 6162-6705 | 88496 | opening | The pin class: redirect every constant composed from the live data root, not only `SNAPSHOT_PATH` *(Helm lane; opened 2026-10-12 by the pipe, per Q43's own closing sentence — "extend the redirect … | DONE 2026-10-12 — `recoveries/helm-root-redirect.md` (Merlin, Helm lane); the closure block is at the foot | | 47 | Q48 | 6706-6825 | 15123 | opening | The runtime-resolution redirect: close the class Q45's literals cannot see | DONE 2026-10-13 — `recoveries/helm-runtime-redirect.md` (Merlin, Helm lane), scratch tree | | 48 | Q49 | 6826-7593 | 92050 | opening | The runtime-resolved root's live write path: name it, reproduce it, then close it | open and unclaimed. Opened by the pipe 2026-10-13 | -| 49 | Q45 | 7594-10538 | 525440 | opening | §9 follow-on — the home-directory channel *(Helm lane; Merlin)* | OPEN — awaiting Alexander's zoom/whole-shop answer; build is the pipe's. | +| 49 | Q45 | 7594-10554 | 529776 | opening | §9 follow-on — the home-directory channel *(Helm lane; Merlin)* | OPEN — awaiting Alexander's zoom/whole-shop answer; build is the pipe's. | ## Ids with no `## ` heading @@ -71,9 +71,9 @@ Opened as records inside another section. `line` is the record's own opening lin | Q50 | 6990 | 6990 | 46 | Q49 | BULLET+BOLD | BOLD-LEAD | | Q51 | 7106 | 7106 | 37 | Q49 | BULLET+BOLD | BOLD-LEAD | | Q52 | 7167 | 7167 | 90 | Q49 | BULLET+BOLD | BOLD-LEAD | -| Q53 | 7292 | 7246 | 150 | Q49 | BULLET+BOLD | BOLD-LEAD | +| Q53 | 7292 | 7246 | 151 | Q49 | BULLET+BOLD | BOLD-LEAD | | Q63 | 9918 | 9909 | 17 | Q45 | BULLET+BOLD | BOLD-LEAD | -| Q64 | 10308 | 10308 | 13 | Q45 | BULLET+BOLD | BOLD-LEAD | +| Q64 | 10308 | 10308 | 14 | Q45 | BULLET+BOLD | BOLD-LEAD | **Not in this document.** Ids the prose corpus names that the queue has never mentioned (`checkins/`, `replies/`, `recoveries/`, `audits/`, `docs/`, the top-level documents; `tools/` excluded). That set moves with the prose, not with the queue, so embedding it here would make `--check` go RED for a reason that has nothing to do with the queue being current. It is reported on demand: `python3 tools/queue_index.py --outside`. @@ -218,7 +218,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q53", "line": 7292, - "refs": 150, + "refs": 151, "section": "Q49", "shape": "BULLET+BOLD" }, @@ -236,7 +236,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q64", "line": 10308, - "refs": 13, + "refs": 14, "section": "Q45", "shape": "BULLET+BOLD" } @@ -246,9 +246,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 1417701, - "queue_lines": 10538, - "queue_sha256": "630b8b3f6732163d2f722aa0785dbb32217b6026f11d315d3f79b014c000d208", + "queue_bytes": 1422037, + "queue_lines": 10554, + "queue_sha256": "7d367619ac1c9213f714d47c201838df184e7a4fb0c9d0fb88e1ff7bd95a8ab6", "sections": [ { "bytes": 3139, @@ -827,11 +827,11 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "The runtime-resolved root's live write path: name it, reproduce it, then close it" }, { - "bytes": 525440, - "end_line": 10538, + "bytes": 529776, + "end_line": 10554, "id": "Q45", "line": 7594, - "lines": 2945, + "lines": 2961, "role": "opening", "role_detail": "", "status": "OPEN \u2014 awaiting Alexander's zoom/whole-shop answer; build is the pipe's.", diff --git a/tools/tool_map.py b/tools/tool_map.py index a58926d..99f2a308 100644 --- a/tools/tool_map.py +++ b/tools/tool_map.py @@ -2,12 +2,12 @@ """tool_map.py -- the instruments get a map, and the map is generated from the tree. WHY THIS EXISTS - `tools/` holds sixteen scripts and there is no document that says what they - are. A reader meets them one at a time -- in a `verify.py` leg, in a queue - block, in a reply -- and nothing states the whole set: which tool grades what, - which flags each one actually accepts, which of them the two probe axes cover, - and which leg of the operator's gate a failure shows up in. The closest thing - is `verify.py`'s `LEGS` table, which names five of the sixteen. + `tools/` holds one script per instrument and there is no document that says + what they are. A reader meets them one at a time -- in a `verify.py` leg, in a + queue block, in a reply -- and nothing states the whole set: which tool grades + what, which flags each one actually accepts, which of them the two probe axes + cover, and which leg of the operator's gate a failure shows up in. The closest + thing is `verify.py`'s `LEGS` table, which names six of them. A hand-written map would be a fourth notation of the defect this suite keeps grading: prose about the tree, one step from the thing it describes, with no @@ -39,6 +39,42 @@ WHAT IT MEASURES -- one verdict per invocation docstring, flags or probe rows without re-rendering the map turns the sweep RED instead of leaving a stale document that reads as current. +THE PRE-COMMIT BLIND SPOT, AND THE MODE THAT CLOSES IT + Every cell above is derived from a REVISION, not from the working tree: + `collect()` reads blobs with `git show :` and runs `--help` from a + copy of that blob. That is right for a document that makes a claim about a + commit, and wrong for the ten minutes before a commit exists. + + Measured on this repository, 2026-10-21. `4602bd5` is drifted: its commit + message states "TOOLS.md re-rendered, byte-identical" and the map it ships is + not the render of its own tree. Two runs, both from inside a worktree checked + out at that commit: `--repo . --check docs/TOOLS.md` reads **rc 1, DRIFT at + line 49** (the `write-axis` cell, whose description moved in `verify.py`), + and `--repo . --selftest` reads **15/16, rc 1**, the one failure being the + currency cell below. Before the commit existed both commands read rc 0 and + 16/16, because the document on disk and the tools at `HEAD` (`ea8fe24`) were + still the pre-edit pair -- the edit lived only in the worktree. + + The gate did not catch it either. `selftest_sweep.py --repo .` at `4602bd5` + reads **21 tools, 21 OK, 0 fatal**, because the sweep runs each tool as + `[python, path, "--selftest"]` with no `--repo` (`selftest_sweep.py:449`) and + this tool's `DEFAULT_REPO` is an absolute path, so its selftest graded + /root/troupe-standup -- a different, clean tree. That is a second defect, and + it is NOT fixed here: the sweep's selftest axis can grade the wrong tree. It + is recorded because it is why the pre-commit runs stayed green. + + `--rev WORKTREE` derives the map from the files on disk, discovered through + `git ls-files -- tools/` -- the index, which is what a commit of tracked + files will carry. Measured on a clean worktree at `190d1aa`: rc 0, and + byte-identical to what HEAD renders; with one uncommitted line appended to + `verify.py`'s `write-axis` description, `--rev HEAD` still reads rc 0 and + `--rev WORKTREE` reads **rc 1, DRIFT at line 49**. The currency cell stays + HEAD-scoped by design: a worktree mid-edit is not a fault, and a sweep that + fails while a docstring is being typed teaches nothing. The pre-commit guard + is that `--check` is run in WORKTREE mode before the commit; four cells below + measure that it separates the two trees and that a clean worktree renders + byte-for-byte what HEAD renders, so the mode cannot invent a drift of its own. + READ-ONLY except for the one path it is told to write. The only process it starts is ` --help`, which does no work by construction; it never runs a documented command, so it cannot write, post, or deploy anything. @@ -52,8 +88,9 @@ EXIT CODES (the tools/ contract) USAGE python3 tool_map.py --repo . --write docs/TOOLS.md # render python3 tool_map.py --repo . --check docs/TOOLS.md # grade drift, rc 1 on drift + python3 tool_map.py --repo . --rev WORKTREE --check docs/TOOLS.md # ...as it will be committed python3 tool_map.py --repo . --json out.json - python3 tool_map.py --selftest # 16 cells + python3 tool_map.py --selftest # 20 cells """ from __future__ import annotations @@ -70,6 +107,10 @@ DEFAULT_REPO = "/root/troupe-standup" DEFAULT_DOC = "docs/TOOLS.md" HELP_TIMEOUT = 30 +# The one value of `--rev` that does not name a revision: the files git would +# commit from this worktree (the index lists them, the disk supplies them). +WORKTREE = "WORKTREE" + # A long option, or a short one, as argparse prints them in the usage block. # `-h`/`--help` are dropped: every tool has them, and a column of sixteen # identical pairs is noise, not information. @@ -84,13 +125,29 @@ def git(repo, *args, inp=None): def read_blob(repo, rev, path): + """The file's text at `rev`; from disk when `rev` is WORKTREE.""" + if rev == WORKTREE: + try: + with open(os.path.join(repo, path), encoding="utf-8") as fh: + return fh.read() + except OSError: + return None rc, out, err = git(repo, "show", f"{rev}:{path}") return out if rc == 0 else None def tracked_tools(repo, rev): - """Sorted `tools/*.py` basenames tracked at `rev`.""" - rc, out, _ = git(repo, "ls-tree", "-r", "--name-only", rev, "--", "tools/") + """Sorted `tools/*.py` basenames tracked at `rev`. + + WORKTREE is not a revision and has no tree object: the set is the INDEX + (`git ls-files`), which is what a commit of tracked files will carry, and + the contents come from disk. Reading the index rather than a reflog of the + worktree keeps one rule -- "the file git would commit" -- for both halves. + """ + if rev == WORKTREE: + rc, out, _ = git(repo, "ls-files", "--", "tools/") + else: + rc, out, _ = git(repo, "ls-tree", "-r", "--name-only", rev, "--", "tools/") if rc != 0: return None names = [p.split("/")[-1] for p in out.split("\n") @@ -417,6 +474,34 @@ def selftest(repo): cell("--repo a directory that is not a git work tree: rc 2", rc_norepo == 2, f"rc {rc_norepo}") + # -- WORKTREE: the tree you are about to commit, not the last commit -- + # The map is derived from a revision by construction, so a docstring + # edited after the last commit is invisible to both `--check` and the + # currency cell below. That is measured here, not described: the same + # document is current against HEAD and drifted against the worktree. + cell("WORKTREE: a clean worktree renders exactly what HEAD renders", + render(collect(fixture, WORKTREE)) == text) + fake = os.path.join(fixture, "tools", "fake_tool.py") + with open(fake, encoding="utf-8") as fh: + src = fh.read() + with open(fake, "w", encoding="utf-8") as fh: + fh.write(src.replace("which nothing measured", + "which nothing has measured yet")) + wt = collect(fixture, WORKTREE) + hd = collect(fixture, "HEAD") + purpose_wt = {r["tool"]: r["purpose"] for r in wt["rows"]}["fake_tool.py"] + purpose_hd = {r["tool"]: r["purpose"] for r in hd["rows"]}["fake_tool.py"] + cell("WORKTREE: reads the file on disk, not the blob at HEAD", + purpose_wt != purpose_hd and "has measured yet" in purpose_wt, + f"worktree={purpose_wt!r} head={purpose_hd!r}") + rc_head = compare(doc, render(hd), quiet=True) + rc_wt = compare(doc, render(wt), quiet=True) + cell("blind spot: an uncommitted docstring edit is rc 0 at HEAD and rc 1 at WORKTREE", + rc_head == 0 and rc_wt == 1, f"HEAD rc {rc_head}, WORKTREE rc {rc_wt}") + rc_wt_norepo = graderc(td, rev=WORKTREE) + cell("WORKTREE outside a git work tree: rc 2, not a crash", + rc_wt_norepo == 2, f"rc {rc_wt_norepo}") + # -- the currency cell: the reason this document stays true ---------- here = os.path.join(repo, "docs", "TOOLS.md") try: @@ -436,11 +521,13 @@ def selftest(repo): return 0 if passed == len(cells) else 1 -def graderc(td): +def graderc(td, rev=None): """rc of a full `--check` run against a directory that is not a repo.""" - proc = subprocess.run([sys.executable, os.path.abspath(__file__), - "--repo", td, "--check", os.path.join(td, "x.md")], - capture_output=True, text=True) + cmd = [sys.executable, os.path.abspath(__file__), "--repo", td] + if rev: + cmd += ["--rev", rev] + cmd += ["--check", os.path.join(td, "x.md")] + proc = subprocess.run(cmd, capture_output=True, text=True) return proc.returncode @@ -488,10 +575,11 @@ def main(): if rc != 0: print(f"tool_map: cannot read repo: {args.repo} is not a git work tree") return 2 - rc, _, err = git(args.repo, "rev-parse", "--verify", f"{args.rev}^{{commit}}") - if rc != 0: - print(f"tool_map: cannot read rev {args.rev}: {err.strip().splitlines()[0] if err.strip() else 'no such object'}") - return 2 + if args.rev != WORKTREE: + rc, _, err = git(args.repo, "rev-parse", "--verify", f"{args.rev}^{{commit}}") + if rc != 0: + print(f"tool_map: cannot read rev {args.rev}: {err.strip().splitlines()[0] if err.strip() else 'no such object'}") + return 2 data = collect(args.repo, args.rev) text = render(data)