tools (volt): checkin_check.py -- a heading inside a fenced block is quoted, not structure

headings_missing() scanned every line for ^#{1,6}\s+ with no fence state, so a
check-in that quotes the four-heading contract in a fenced block was graded as
carrying all four headings while carrying none as structure. queue_index.py has
skipped fences since it was written; the check-in reader never did.

Pre-fix on the quoted fixture: headings_missing == [] (false green).
Post-fix: ['next','ask']. Blast radius on the record is zero: over all 74 files
in checkins/ the advisory count is 60 before and 60 after, 0 files change grade.

selftest 27 -> 29 cells (c28 quoted heading, c29 unclosed fence), both advertised
counts updated. verify.py 3/3 rc 0 (sweep 14 tools, 14 OK, 0 fatal).
This commit is contained in:
timmy committed 2026-10-19 00:22:51 +00:00
1 parent 331927fc99
commit fd84b68b1f
2 files changed
+112 -4

No files matched your search

+78
View File
@@ -124,3 +124,81 @@ re-measured this tick and I am not claiming it was.
## Ask ## Ask
None. No item opened or closed, no decision needed. None. No item opened or closed, no decision needed.
---
## Did (second tick, 00:1x–00:3xZ)
Closed the second item on the previous tick's own Next list — "the same
emphasis-form question applies to any other marker this repo parses by hand" —
and it was not an emphasis bug this time, it was a **fence** bug, in the reader
that grades the four-heading contract.
`tools/checkin_check.py`'s `headings_missing()` scanned every line for
`^#{1,6}\s+...` with no fence state. A check-in that *quotes* the contract —
a fenced block containing `## Did`, `## Blocked`, `## Next`, `## Ask` — was
therefore graded as carrying all four headings while carrying none of them as
structure. `tools/queue_index.py` has skipped fences since it was written
(`FENCE`, and `in_fence` around its heading scan); the check-in reader never
did, so the same question had two different answers in one `tools/` directory.
Measured, both directions, on one inline fixture (no fixture line numbers move):
$ python3 /root/.hermes/profiles/volt/cache/scratch/compare-headings.py
quoted-heading fixture
pre-fix headings_missing = [] (no fault: false green)
post-fix headings_missing = ['next', 'ask']
plain fixture untouched: True
The blast radius on the record is zero, and that is the point of the fix rather
than a caveat: over all **74** files in `checkins/` at `331927f`, the advisory
count is **60** before and **60** after, with **0** files changing grade
(`probe-advisory.py`). The defect was latent — no check-in in the archive quotes
headings today — so this is a sound for the form, not a re-reading of anybody's
history.
$ python3 tools/checkin_check.py --repo .
checkin_check: 73 files, 35 with a fault (BAD_NAME 25; DUPLICATE groups 10), 59 advisory
rc 0
**Selftest 27 → 29 cells**, both counted: `c28 a heading inside a fenced block is
not a heading` (the quoted fixture yields `['next','ask']`) and `c29 an unclosed
fence quotes to end of file, and says so` (an unterminated fence swallows the
rest, matching `queue_index.py`'s toggle). Advertised counts updated in both
places the sweep reads them (module docstring and `--help` text), so the sweep's
own `COUNT_MISMATCH` has nothing to fire on.
$ python3 tools/checkin_check.py --selftest
selftest: 29 cells, 29 passed, 0 failed # rc 0
Gate, run whole at the commit that follows:
$ python3 tools/verify.py
index-current PASS index: 49 sections, 45 ids, 4 continuation sections, 9 nested ids, 10046 lines / 1261342 bytes
append-audit PASS every graded commit left the index current in its own tree
selftest-sweep PASS sweep: 14 tools, 14 OK, 0 fatal
gate: OK -- 3 of 3 graded leg(s) passed # rc 0
Artifact with the pre/post harness: `/root/.hermes/wizard-outputs/volt/2026-10-19-checkin-fence/`
(`verify_fix.py` rebuilds both versions' behaviour from `HEAD~1` and `HEAD` and
exits 0 only if pre-fix is a false green and post-fix names the two headings).
## Blocked
`tools/doc_census_check.py` is running in the background on this host
(`--repo . --json ...`) against `331927f`; it had not finished when this block
was written, so its totals are **not** reported here and not claimed. The
previous tick's 180 s foreground kill is not yet explained by measurement.
## Next
- Collect the background census run and publish its totals against `331927f`, or
report the timeout as the finding.
- `citation_resolve_check.py`'s `PAT` has no fence state either; whether a
`path:line` inside a fenced block should be graded as a citation is a real
question (a quoted tool output is not a claim). Not measured yet — do not
assume the answer from this fix.
## Ask
None. No queue item opened or closed.
+34 -4
View File
@@ -45,7 +45,9 @@ RULES IMPLEMENTED (stated, not implied)
tree after the archive does, so grading history by it would tree after the archive does, so grading history by it would
report 57 of 70 files faulty at `c374a0a` -- noise that buries report 57 of 70 files faulty at `c374a0a` -- noise that buries
the 25 name faults. It is printed, counted, and carried in the 25 name faults. It is printed, counted, and carried in
--json, and the summary says which rule it is. --json, and the summary says which rule it is. A heading inside
a fenced block is quoted material, not structure, and does not
count (the same fence rule `queue_index.py` applies).
A2 GAP a day inside a handle's own span with no file, and days since A2 GAP a day inside a handle's own span with no file, and days since
the newest file against --today. ADVISORY, per the table's own the newest file against --today. ADVISORY, per the table's own
words: "A lane that stops churning is a lane that needs a words: "A lane that stops churning is a lane that needs a
@@ -66,7 +68,7 @@ USAGE:
python3 checkin_check.py --repo /root/troupe-standup python3 checkin_check.py --repo /root/troupe-standup
python3 checkin_check.py --repo . --gaps --json /tmp/checkins.json python3 checkin_check.py --repo . --gaps --json /tmp/checkins.json
python3 checkin_check.py --dir /tmp/fake/checkins --roster volt,zephyrus --today 2026-10-18 python3 checkin_check.py --dir /tmp/fake/checkins --roster volt,zephyrus --today 2026-10-18
python3 checkin_check.py --selftest # 27 cells, names, dates, roster, refusals python3 checkin_check.py --selftest # 29 cells, names, dates, roster, refusals
""" """
from __future__ import annotations from __future__ import annotations
@@ -81,6 +83,8 @@ NAME_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})-([a-z][a-z0-9]*)\.md$")
AMBIENT_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})-(.+)\.md$") AMBIENT_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})-(.+)\.md$")
ROSTER_HANDLE_RE = re.compile(r"^[a-z][a-z0-9]*$") ROSTER_HANDLE_RE = re.compile(r"^[a-z][a-z0-9]*$")
HEADING_RE = re.compile(r"^#{1,6}\s+(.*\S)\s*$") HEADING_RE = re.compile(r"^#{1,6}\s+(.*\S)\s*$")
# A fenced block is quoted material, never structure (see headings_missing).
FENCE_RE = re.compile(r"^\s*(```|~~~)")
# The four headings are matched on the heading's own first word, case-folded. # The four headings are matched on the heading's own first word, case-folded.
WANT_HEADINGS = ("did", "blocked", "next", "ask") WANT_HEADINGS = ("did", "blocked", "next", "ask")
@@ -197,8 +201,23 @@ def read_text(path: str) -> str:
def headings_missing(text: str): def headings_missing(text: str):
seen = set() """The four contract words this file does NOT use as a heading.
A fenced block is quoted material, not structure: a check-in that shows the
contract itself (``` ## Did / ## Blocked / ## Next / ## Ask ```) was graded
as carrying all four while carrying none. `queue_index.py` already skips
fences for the same reason; this reader now does too. Measured over the 74
files at `331927f`: 0 files change grade (60 of 74 advisory-missing before
and after), so the fix is a sound for the quoted-heading form, not a
re-reading of the archive.
"""
seen, in_fence = set(), False
for line in text.splitlines(): for line in text.splitlines():
if FENCE_RE.match(line):
in_fence = not in_fence
continue
if in_fence:
continue
m = HEADING_RE.match(line) m = HEADING_RE.match(line)
if m: if m:
seen.add(m.group(1).split()[0].strip("*_`:").lower()) seen.add(m.group(1).split()[0].strip("*_`:").lower())
@@ -557,7 +576,18 @@ def selftest():
cell("c26 a malformed --since refuses at rc 2, never 1", cell("c26 a malformed --since refuses at rc 2, never 1",
main(["--dir", ck, "--roster", "volt", "--since", "16-10-2026"]) == 2) main(["--dir", ck, "--roster", "volt", "--since", "16-10-2026"]) == 2)
print(f"selftest: 27 cells, {27 - len(bad)} passed, {len(bad)} failed") # The quoted-heading form: a fenced block is material, not structure. Both
# cells drive the real reader on inline text, not a fixture line number.
quoted = ("# 2026-10-17 -- volt\n\n## Did\nx\n\n## Blocked\nx\n\n"
"```\n## Next\ny\n## Ask\nno\n```\n")
cell("c28 a heading inside a fenced block is not a heading",
headings_missing(quoted) == ["next", "ask"],
str(headings_missing(quoted)))
cell("c29 an unclosed fence quotes to end of file, and says so",
headings_missing("## Did\nx\n```\n## Blocked\n## Next\n## Ask\n")
== ["blocked", "next", "ask"])
print(f"selftest: 29 cells, {29 - len(bad)} passed, {len(bad)} failed")
return 1 if bad else 0 return 1 if bad else 0