diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index fa37e91..7703e9a 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -10429,3 +10429,25 @@ Measured now against the live forge API (per-repo `state=open`), against the com **3. Carried, not decided here:** the 7 published-archive corrections (magazine-021, -011 ×3, -061 ×2, -040, -041) graded contradicted or unsupported remain **Alexander's publish decisions**, open since 2026-09-26. Neither this correction nor any automated grading changes that; the queue now states them, it does not decide them. No issue, PR, release or deploy surface touched; no secret printed. + +**path_claim_check 2026-10-21 (volt, Workhorse) — the default its USAGE block advertised is now the default the code applies: the bare run equals the named run, and the archive's false RED is gone.** No item opened or closed; the open set is unchanged (`Q37:5278` pipe, career-lane `Q53:7303` held for Alexander, `Q64`'s design question Alexander's). Appended at the foot with `docs/queue-index.md` re-rendered in the same commit. + +1. **The defect, measured on both sides before anything was edited.** At `6cd2114`, `path_claim_check.py --repo .` printed **1047 distinct paths, 10 missing, 10 new against the baseline**, and under `--fail-on-missing` returned **rc 1**; the same tree, same revision, with `--baseline tools/path_claim_baseline.tsv` printed **0 new** and returned **rc 0**. Root cause read in the source: `ap.add_argument("--baseline")` carried no `default=` while the USAGE block advertised the ledger path, so a bare invocation graded against an empty ledger and turned every tolerated row into a fresh defect. The correction appended at `481052b` had already traced this and named the one-line fix; this record is that fix landing, with the cell the correction asked for. + +2. **What landed — `--baseline` defaults to `tools/path_claim_baseline.tsv`, resolved against `--repo`, never against the caller's cwd.** Measured from `/tmp` against `/root/troupe-standup`: **0 new, rc 0**. Two behaviours are deliberately kept distinct, because collapsing them is how a default becomes a silent pass: + - a caller who **names** a ledger that is not there is still **rc 2** — `cannot read baseline /nope/led.tsv` — since a mistyped ledger must never read as "no ledger"; + - a tree that simply **carries none** (the selftest fixtures, a clone without the ledger) is graded with no ledger after one note on stderr, so the census still prints and its count is still the finding. That is the behaviour the fixture cells already depended on, now stated instead of accidental. + +3. **Selftest 19 → 20, and the new cell fails on the old code.** `c20` writes a ledger into the fixture repo, commits it, then asserts the bare run and the named run return the **same rc and the same summary line** (`1 matched`, `0 new`, rc 0). Under the previous build that comparison is rc 1 versus rc 0 — which is what makes it a test rather than a decoration. Measured: `selftest: 20 cells, 20 passed, 0 failed, rc 0`. + +4. **The gate still bites, and the refusals still refuse.** An empty ledger over the live corpus → **10 new**, `FAIL: 10 missing path(s) the baseline does not name`, **rc 1**. `/nope/led.tsv` → `cannot read baseline`, **rc 2**. The default removes a false RED; it does not remove the RED. + +5. **Three docstring statements had to move with the code.** USAGE now states the default and drops the redundant explicit flag from the example command (the example is the bare form, which is now the correct form); the exit-code block distinguishes a **named** baseline that cannot be read (rc 2) from a repo carrying none (not rc 2); the cell count moved 19 → 20 in the same edit, so `doc_census_check.py` keeps a MATCHES claim instead of turning STALE. + +6. **Numbers carry the revision they were read at, because two of them differ from the correction block.** At `6cd2114`: 431 markdown files, **1047 distinct paths / 4394 occurrences**, 306 EXISTS, 10 MISSING, 721 EXTERNAL, 10 TEMPLATE, 2374 fenced lines skipped; ledger **10 entries, 10 matched, 0 new**. The correction measured **3 new** explicitly at `028e2d9`; at HEAD it is **0 new**, and that is not a contradiction — `f31c59d` landed the three rows (2× EXAMPLE, 1× PLANTED) that excuse the remaining paths. Same method, moved tree; a count quoted beside a path list names its tree. + +7. **What it does not claim.** A default is not a review: the ten tolerated rows are still exceptions a reader can see, printed with their class on every run, and `--fail-on-missing` is green because the ledger excuses them, not because the paths exist. The 721 EXTERNAL paths remain counted and never graded. And the gate is not green overall: `verify.py` reads **5 legs, 4 pass, 1 fault**, the fault being `checkin` on zephyrus's two 2026-10-19 files — pre-existing at `028e2d9`, unchanged by this landing, and not this lane's to rename. + +Verification, read at `6cd2114` (the revision this record was written against; appending the record itself moves the census by the paths it names): `path_claim_check.py --selftest` **20/20 rc 0**; bare run **1047 distinct / 10 missing / 0 new, rc 0**, identically from `/tmp`; empty ledger **10 new, rc 1**; named-but-absent ledger **rc 2**; `doc_census_check.py --tools tools/path_claim_check.py --fail` **0 INTERNAL SPLIT, 0 STALE, rc 0**; `doc_command_check.py` **61 commands over 18 oracles, OK 61, rc 0**; `selftest_sweep.py` **20 tools, 20 OK, 0 fatal, rc 0** with both probe axes **25/25** and **23/23**; `tool_map.py --check docs/TOOLS.md` **rc 0**; `verify.py` **5 legs, 4 pass, 1 fault** (`checkin`, pre-existing, named above). + +— Volt diff --git a/docs/queue-index.md b/docs/queue-index.md index 1362c57..f5f7e81 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-10431 | 495343 | 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-10453 | 500400 | 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 | 144 | Q49 | BULLET+BOLD | BOLD-LEAD | +| Q53 | 7292 | 7246 | 145 | Q49 | BULLET+BOLD | BOLD-LEAD | | Q63 | 9918 | 9909 | 17 | Q45 | BULLET+BOLD | BOLD-LEAD | -| Q64 | 10308 | 10308 | 7 | Q45 | BULLET+BOLD | BOLD-LEAD | +| Q64 | 10308 | 10308 | 8 | 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": 144, + "refs": 145, "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": 7, + "refs": 8, "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": 1387604, - "queue_lines": 10431, - "queue_sha256": "b518d1c726a1967105e2370911dde2a0bdd52484cfb8a6696acc0e7c7adde354", + "queue_bytes": 1392661, + "queue_lines": 10453, + "queue_sha256": "80599066201821721864f30cd0900768a47f6e2512a6a93a6de7c8108201db32", "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": 495343, - "end_line": 10431, + "bytes": 500400, + "end_line": 10453, "id": "Q45", "line": 7594, - "lines": 2838, + "lines": 2860, "role": "opening", "role_detail": "", "status": "OPEN \u2014 awaiting Alexander's zoom/whole-shop answer; build is the pipe's.", diff --git a/tools/path_claim_check.py b/tools/path_claim_check.py index cb2f425..eef8352 100644 --- a/tools/path_claim_check.py +++ b/tools/path_claim_check.py @@ -66,16 +66,20 @@ EXIT CODES 1 --fail-on-missing and at least one MISSING path is not in the baseline, or names a class the ledger does not tolerate. 2 could not grade: not a git work tree, a revision that does not resolve, a - corpus with no markdown, a baseline that cannot be read or names an - unknown class. Never 0, and never a silent empty census. + corpus with no markdown, a NAMED baseline that cannot be read or names an + unknown class. Never 0, and never a silent empty census. A repo that + carries no ledger of its own is not this: `--baseline` defaults to + tools/path_claim_baseline.tsv resolved against --repo, and a tree without + that file is graded with no ledger after one note on stderr. USAGE python3 path_claim_check.py [--repo DIR] [--rev HEAD] [--corpus PATH...] [--json OUT] [--only missing|exists|external|template|all] [--context] [--fail-on-missing] [--baseline PATH] - python3 path_claim_check.py --selftest # 19 cells - python3 path_claim_check.py --repo . --fail-on-missing \\ - --baseline tools/path_claim_baseline.tsv + --baseline defaults to tools/path_claim_baseline.tsv, resolved against + --repo, so the bare form already grades against this archive's ledger. + python3 path_claim_check.py --selftest # 20 cells + python3 path_claim_check.py --repo . --fail-on-missing """ import argparse import collections @@ -87,6 +91,16 @@ import sys DEFAULT_REPO = "/root/troupe-standup" +# The ledger this archive carries, REPO-relative: resolved against --repo, not +# against the caller's cwd, so a bare run against a clone reads that clone's +# ledger. Until 2026-10-21 this was advertised in USAGE and never applied -- +# `--baseline` had no default, so a bare invocation graded against an empty +# ledger and, under --fail-on-missing, returned rc 1 for an archive whose own +# rows excuse seven of the ten MISSING paths. A default a reader can cite and +# the code does not apply is the worst kind: it makes the tool lie in the +# direction of a defect. +DEFAULT_BASELINE = "tools/path_claim_baseline.tsv" + # The extensions this archive actually names. A closed list, for the reason # citation_resolve_check.py gives: an open `\\.[a-z]+` swallows clock times, # decimals and domain names. @@ -319,6 +333,30 @@ def report(rows, stats, rev, sha, corpus, ledger, path, only, context): print(" ...%s..." % r["context"]) +def resolve_baseline(repo, path): + """The ledger to grade against, or None when this repo carries none. + + `path` is repo-relative by contract (DEFAULT_BASELINE), so it is joined onto + `repo` before the existence test: a bare run from another cwd must read the + tree it is grading, never a file beside the caller. An absolute path is + taken as given. A caller who NAMED a ledger that is not there gets the path + back unchanged, so load_baseline refuses it with rc 2 -- a mistyped ledger + must never read as "no ledger". A repo that simply carries none (a fixture + tree) is a stated reading: one note on stderr, no ledger, and the census + still prints, because the count is the finding. + """ + if not path: + return None + cand = path if os.path.isabs(path) else os.path.join(repo, path) + if os.path.exists(cand): + return cand + if path == DEFAULT_BASELINE: + print("note: no ledger at %s -- every MISSING path counts as new" % cand, + file=sys.stderr) + return None + return cand + + def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) @@ -330,7 +368,9 @@ def main(): "all"], default="missing") ap.add_argument("--context", action="store_true") ap.add_argument("--fail-on-missing", action="store_true") - ap.add_argument("--baseline") + ap.add_argument("--baseline", default=DEFAULT_BASELINE, + help="ledger to grade MISSING rows against (default: " + "%s, resolved against --repo)" % DEFAULT_BASELINE) ap.add_argument("--selftest", action="store_true") a = ap.parse_args() @@ -349,7 +389,8 @@ def main(): if not corpus: raise CannotGrade("cannot grade: no markdown corpus at %s in %s" % (a.rev, a.repo)) - ledger = load_baseline(a.baseline) if a.baseline else None + ledger = resolve_baseline(a.repo, a.baseline) + ledger = load_baseline(ledger) if ledger else None rows, stats = scan(a.repo, a.rev, corpus, store) except CannotGrade as exc: print(str(exc), file=sys.stderr) @@ -517,6 +558,29 @@ def selftest(): rc == 0 and "tolerated EXAMPLE" in out and "1 matched" in out, "rc=%d %s" % (rc, out[-300:])) + # --- the default ledger is the one the tree carries --------------------- + # Until 2026-10-21 `--baseline` had NO default while USAGE advertised one: + # a bare run graded against an empty ledger, so `--fail-on-missing` returned + # rc 1 for a tree whose own rows excused the path. This pins bare == named. + with open(os.path.join(repo, "tools", "path_claim_baseline.tsv"), "w") as fh: + fh.write("# fixture: the ledger this tree carries\n" + "docs/absent.md\tPLANTED\tfixture row, carried by the tree\n") + subprocess.run(["git", "-C", repo, "add", "-A"], capture_output=True) + subprocess.run(["git", "-C", repo, "-c", "user.email=t@t", "-c", "user.name=t", + "commit", "-qm", "ledger"], capture_output=True) + + def summary(text): + return [ln for ln in text.splitlines() if ln.startswith("path_claim_check:")] + + bare_rc, bare_out = run("--repo", repo, "--fail-on-missing") + named_rc, named_out = run("--repo", repo, "--fail-on-missing", "--baseline", + os.path.join(repo, "tools", "path_claim_baseline.tsv")) + cell("c20 the repo's own ledger is the default: bare run == named run", + bare_rc == named_rc == 0 and summary(bare_out) == summary(named_out) + and "1 matched" in bare_out, + "bare rc=%d %r | named rc=%d %r" + % (bare_rc, summary(bare_out), named_rc, summary(named_out))) + print("selftest: %d cells, %d passed, %d failed" % (n, n - bad, bad)) return 0 if bad == 0 else 1