doc_census_check (volt): a number ending a line and its unit opening the next is ONE claim -- joined only when structural, spent only when a leg prints that unit, every unbound pair named; 16-module-docstring A/B 134->140 (+6, -0), 4-tool census +1 MATCHES and 0 verdicts moved, selftest 82->104 cells

This commit is contained in:
volt committed 2026-10-20 14:35:07 +00:00
1 parent 3487b2b4ea
commit 495a591e08
4 files changed
+375 -19

No files matched your search

+301 -10
View File
@@ -48,6 +48,33 @@ WHAT IT MEASURES (read-only: this tool only writes --json)
A claim is `<digits> <unitword>` with the unit word ADJACENT, which is the
archive's own notation. A bare number is not a claim and is not checked.
A WRAPPED PAIR IS A CLAIM ONLY WHEN A LEG CAN BIND IT. A number that ENDS one
line with its unit opening the next is the same claim the archive writes on
one line, and until 2026-10-20 this reader was line-local: a docstring reflow
retired the claim silently while every command exited 0 (measured -- the
pinned pair in `queue_claim_check.py` fell out of the census and the claim
count fell with it, with no verdict moving). The reader now joins the pair,
under the same refusals the adjacency reader already applies: a number welded
to a preceding `-`, `/`, `.`, `:`, `_` or letter is not standalone, so a
date's tail and `UTF-8` and `0/1` are not counts, and a unit with a hyphen
behind it is a compound adjective. Whether the joined pair is SPENT is a
second question, and it is answered by the census rather than by a lexicon of
English: the pair is graded only when some leg prints its unit UNDER THAT
SPELLING, and every pair no leg binds is printed as WRAP_UNBOUND and counted
in the banner, never dropped. The spelling is exact and that is deliberate: a
plural fold (`notes` -> `note`) is affordable for a one-line claim, because
both sides are census voice, but a wrap is this reader's hypothesis about two
lines of prose. Measured at HEAD 2026-10-20, the fold spent `the queue's own
reply 62 / notes it` and graded a correct sentence STALE 62-vs-7; with the
fold refused, that pair is WRAP_UNBOUND and the one wrapped claim a leg really
binds -- `1,062 CONTENT_SHA256` at `citation_hash_class.py:53` -- is the only
one graded. The other five candidates are named, not graded: `69 filenames`
(`checkin_check.py:14`), `18 cells`, `24 spendable` and `0 without` (all
three in this file -- its `--selftest`, fast-mode and `--fail` paragraphs),
and `62 notes` (`queue_claim_check.py:119`). With `--no-run` there is no leg, so
no wrap is spent and each one is reported unbound -- a mode that cannot see a
census cannot bind a claim.
TWO ORACLE LEGS, AND WHY THE SECOND ONE EXISTS
Leg 1 is the tool's own run at the block's revision, exactly as above. Leg 2
is the SAME TOOL'S `--selftest`, added 2026-10-16 after the control plane
@@ -220,7 +247,7 @@ FAILING ON THE CLASS IT CATCHES (`--fail`)
USAGE
python3 doc_census_check.py [--repo DIR] [--rev HEAD] [--tools NAME...]
[--json OUT] [--fail] [--no-run] [--vocab PATH] [--save-vocab PATH]
[--selftest] # selftest: 82 cells, one per claim class
[--selftest] # selftest: 104 cells, one per claim class
"""
import argparse
import ast
@@ -290,6 +317,51 @@ CLAIM = re.compile(
r"(?P<unit>[A-Za-z_][A-Za-z0-9_]*|[A-Z][A-Z_]{2,})\b(?!-)")
CLAIM_EQ = re.compile(
r"(?P<unit>[A-Za-z_][A-Za-z0-9_]*)\s*[:=]\s*\**\s*(?P<num>[0-9]+(?:,[0-9]{3})*)")
# A WRAPPED PAIR: the number ENDS one line and its unit OPENS the next. Until
# 2026-10-20 this reader was line-local, so a claim that a docstring's 79-column
# wrap cut in two was not a claim at all. That is not cosmetic: `queue_claim_check
# .py`'s pinned pair `1,229,541 B / 9,953 lines` was broken onto two lines at
# `70fa8b2` and the census silently fell from seventeen claims to sixteen with no
# verdict moving -- the archive's own instrument, blind to the archive's own
# notation, while every command exited 0. Measured at HEAD 2026-10-20 by running
# this reader over all sixteen tracked `tools/*.py` MODULE docstrings -- the only
# scope docstring_span reads: SIX
# number-ends-a-line candidates -- FOUR real counts with their unit
# (`69 filenames` at checkin_check.py:14, `1,062 CONTENT_SHA256` at
# citation_hash_class.py:53, `18 cells` and `24 spendable` in this file's
# `--selftest` and fast-mode paragraphs) and TWO words that merely open a
# sentence (`0 without` in the `--fail` paragraph here, `62 notes` at
# queue_claim_check.py:119). The same reader at `70fa8b2`, where the break was
# introduced, reads seven -- the same six plus the pinned `9,953 lines` pair --
# and spends it, so that docstring reads sixteen claims against the old reader
# and seventeen against this one. `4e6b2ef`, the commit that put the pair back on
# one line, is the control on the other side: it reads six candidates and
# seventeen claims on BOTH readers, so the six is a measurement of HEAD's prose
# and not a fallback the join quietly invents. A twelve-row artifact was produced
# that day by a looser net that counted every number ending a line with any word
# after it, non-counts included; the reader itself reads six.
# The same measurement over FUNCTION and CLASS docstrings -- which this tool does
# not read at all, module docstrings only -- finds two more real counts, `29
# literal` in append_only_audit.py and `74 files` in checkin_check.py. They are
# named here rather than quietly excluded: a scope the reader cannot see is
# worth knowing, and the join would spend either one if the scope ever widened.
#
# WHY THERE IS NO WORD LIST HERE. The obvious reader is "join the wrap unless the
# unit is a verb", and it needs an English lexicon that would refuse a real claim
# the day one is written in an unlucky voice. The discrimination is made where
# this tool already makes it: A WRAPPED PAIR IS SPENT ONLY WHEN A LEG PRINTS ITS
# UNIT (see check_tool). At HEAD that spends exactly one of the six --
# `1,062 CONTENT_SHA256`, whose census prints the label -- and the other five
# (`69 filenames`, `18 cells`, `24 spendable`, `0 without`, `62 notes`) are
# printed unbound and never graded. The structural half below is the part that
# must not be guessed at, and it is the same half the adjacency reader already
# enforces: a number welded to a preceding `-`, `/`, `.`, `:`, `_` or
# alphanumeric is not standalone (`2026-10-18`, `UTF-8` and `0/1` are three of
# the strings this refuses, each one a refusal cell in the selftest below), and
# a unit with a hyphen behind it is a compound adjective, not a unit noun.
WRAP_LEAD = re.compile(r"(?:^|[\s(\[{*`'\"])(?P<num>[0-9]+(?:,[0-9]{3})*)[ \t]*$")
WRAP_UNIT = re.compile(r"^[ \t]*(?P<unit>[A-Za-z_][A-Za-z0-9_]*|[A-Z][A-Z_]{2,})\b(?!-)")
REV_LONG = re.compile(r"\b[0-9a-f]{7,40}\b")
# A hex run introduced as a DIGEST is not a revision, and reading it as one costs
@@ -477,15 +549,20 @@ def ordinal_at(raw, pos):
def claims_in(blk):
"""[(line, value, unit, token, offset)] for one block.
"""[(line, value, unit, token, offset, wrapped)] for one block.
Both claim forms are read and a claim found by both is reported once. The
offset is measured on the block text so a RESTATEMENT (far apart) can be
told from an ENUMERATION of neighbouring sub-counts, the same
discrimination num_agreement_check.py applies.
`wrapped` is True for a claim whose number ends one line and whose unit opens
the next -- see WRAP_LEAD. It is a reading decision only: whether such a pair
is SPENT is decided in check_tool, by whether a leg prints its unit.
"""
out, off = [], 0
out, off, starts = [], 0, []
for ln, raw in blk:
starts.append(off)
found = {}
for m in CLAIM.finditer(raw):
u = m.group("unit")
@@ -504,8 +581,25 @@ def claims_in(blk):
found[m.start()] = (int(m.group("num").replace(",", "")), u, m.group(0))
for pos in sorted(found):
val, u, tok = found[pos]
out.append((ln, val, u, tok, off + pos))
out.append((ln, val, u, tok, off + pos, False))
off += len(raw) + 1
for i in range(len(blk) - 1):
ln, raw = blk[i]
m = WRAP_LEAD.search(raw)
if not m:
continue
um = WRAP_UNIT.match(blk[i + 1][1])
if not um:
continue
u = um.group("unit")
if len(u) < 3 or u.lower() in STOPWORDS or re.fullmatch(r"[qQ]\d+", u):
continue
if ordinal_at(raw, m.start("num")):
continue
num = m.group("num")
out.append((ln, int(num.replace(",", "")), u, f"{num} {u}",
starts[i] + m.start("num"), True))
out.sort(key=lambda r: r[4])
return out
@@ -633,7 +727,7 @@ def _fixture_checks():
for first, blk in blocks(lines, 1, len(lines)):
revs = block_revs("\n".join(r for _, r in blk))
revs_seen |= set(revs)
for ln, val, unit, _tok, _pos in claims_in(blk):
for ln, val, unit, _tok, _pos, _w in claims_in(blk):
got.append((unit, val))
if first == 1:
first_block.append((unit, val))
@@ -646,7 +740,7 @@ def _fixture_checks():
# the tightened separator, one cell per form it now refuses, plus the
# positive control -- a rule that refuses everything proves nothing
def claims_of(text):
return [(u, v) for _ln, v, u, _t, _p in claims_in([(1, text)])]
return [(u, v) for _ln, v, u, _t, _p, _w in claims_in([(1, text)])]
check("claims: a welded digit-and-letter run is not a claim",
claims_of("sha 83acffe0 / rev 9188dd3 at HEAD"), [])
@@ -675,6 +769,57 @@ def _fixture_checks():
check("claims: a count beside a line cite is still a claim",
claims_of("open set: Q37:5278 while 13 rows close"), [("rows", 13)])
check("claims: the block's rev is read", "babdadc" in revs_seen, True)
# THE WRAPPED PAIR, 2026-10-20. A count that ends one line with its unit
# opening the next is the same claim the archive writes on one line, and
# every ADMIT text below is a real wrap copied out of the docstring it was
# measured in -- not an invented example. The four ADMIT cells are the four
# counts at HEAD; the fifth candidate, `62 notes`, is a verb and is settled
# by binding (wrap_bound, the FOLD cells below) rather than by these cells.
# The four refusal cells are the structural half, synthesized rather than
# copied because at HEAD no welded or hyphenated string survives to the
# candidate stage for a text to be lifted from.
def claims_lines(text):
blk = [(i + 1, raw) for i, raw in enumerate(text.split("\n"))]
return [(u, v, w) for _ln, v, u, _t, _p, w in claims_in(blk)]
check("wrap: a number ending a line and its unit opening the next is a claim",
claims_lines("Twenty-five of the 69\nfilenames are not the form"),
[("filenames", 69, True)])
check("wrap: a thousands separator survives the join",
claims_lines("594 BLOB_OID / 1,062\nCONTENT_SHA256 and the rest"),
[("BLOB_OID", 594, False), ("CONTENT_SHA256", 1062, True)])
check("wrap: a backtick before the number and a closing one after the unit",
claims_lines("a selftest that prints `18\ncells` -- the defect class"),
[("cells", 18, True)])
check("wrap: an adjective is a unit when it is the count's own word",
claims_lines("observed keys at 30c8120, 24\nspendable, the other 398"),
[("spendable", 24, True)])
check("wrap: a claim that does not wrap is not tagged",
claims_lines("a selftest that prints 18 cells"), [("cells", 18, False)])
check("wrap: the same text wrapped and unwrapped is two claims, not three",
claims_lines("prints 18 cells\nand 18\ncells"),
[("cells", 18, False), ("cells", 18, True)])
# the four refusals, one cell each -- the structural half no candidate at
# HEAD exercises, because the reader refuses these strings before the join
check("wrap: a date's tail is welded to its hyphens, not a count",
claims_lines("--today 2026-10-18\npython3 tools/x.py"), [])
check("wrap: `UTF-8` ends a line and is still not a count",
claims_lines("a malformed name, non-UTF-8\nbytes are refused"), [])
check("wrap: a slash-separated pair (`0/1`) is not a count",
claims_lines("exit 0/1\npython3 tools/x.py"), [])
check("wrap: a hyphen behind the joined unit is a compound adjective",
claims_lines("the queue read 65 same-party / 9\nother-party. Neither"),
[])
# THE FOLD, as a unit rule: keys_for folds a plural (it must -- a one-line
# claim and a printed label are both census voice), wrap_bound does not.
check("wrap: a plural verb is bound by no fold -- `notes` is not `note`",
wrap_bound("notes", [({"note": {7}}, set(), "")]), False)
check("wrap: and a unit printed under its own spelling IS bound",
wrap_bound("notes", [({"notes": {62}}, set(), "")]), True)
check("wrap: an uppercase label binds to the lowercase printed key",
wrap_bound("CONTENT_SHA256",
[({"content_sha256": {1062}}, set(), "")]), True)
# the digest rule, one signal per cell, and the negative control between
# them -- the text of each is the text of the defect, measured at HEAD
check("revs: `sha256 <hex>` is a digest, not the block's revision",
@@ -906,6 +1051,40 @@ def _fixture_checks():
"\n471 UNRESOLVED at the head.\n" + "pad " * 80 + "\n"
'441 UNRESOLVED at the tail.\n"""\n'
"print('UNRESOLVED: 471')\n")
# THE WRAPPED PAIR, three stubs that differ only in whether a leg can
# bind the unit: agreeing, disagreeing, and a wrap that is prose.
with open(os.path.join(td, "tools", "stub_wrap_ok.py"), "w") as fh:
fh.write('"""A stub whose claim wraps onto the next line.\n'
"\nWe measured 7\n"
'widgets at HEAD.\n"""\n'
"print('widgets: 7')\n")
with open(os.path.join(td, "tools", "stub_wrap_stale.py"), "w") as fh:
fh.write('"""A stub whose wrapped claim disagrees with its census.\n'
"\nWe measured 9\n"
'widgets at HEAD.\n"""\n'
"print('widgets: 7')\n")
with open(os.path.join(td, "tools", "stub_wrap_unbound.py"), "w") as fh:
fh.write('"""A stub whose wrap is prose, not a census.\n'
"\nIt exited 0\n"
'without a census.\n"""\n'
"print('widgets: 7')\n")
# THE FOLD, end to end -- the false fault measured on the real archive at
# HEAD (queue_claim_check.py:119, `reply 62 / notes it`). `notes` folds to
# the printed key `note`, which the loose printed reader picked out of a
# PASS line's prose, so a plural fold would spend this sentence and grade
# it STALE. The pair must land as unbound instead.
with open(os.path.join(td, "tools", "stub_wrap_fold.py"), "w") as fh:
fh.write('"""A stub whose wrap is a verb that folds onto a key.\n'
"\nThe queue's own reply 62\n"
'notes it).\n"""\n'
"print('note: 7')\n")
# ...and the boundary: the SAME text on ONE line is still a claim, still
# folded, still graded -- the narrowing is the wrap's price, not a new
# rule for the adjacency reader.
with open(os.path.join(td, "tools", "stub_note_adjacent.py"), "w") as fh:
fh.write('"""A stub whose one-line claim folds onto a key.\n'
"\nThe queue's own reply 62 notes it).\n\"\"\"\n"
"print('note: 7')\n")
genv = dict(os.environ, GIT_AUTHOR_NAME="t", GIT_AUTHOR_EMAIL="t@t",
GIT_COMMITTER_NAME="t", GIT_COMMITTER_EMAIL="t@t")
for sub in (["init", "-q"], ["add", "-A"], ["commit", "-qm", "fixture"]):
@@ -929,6 +1108,24 @@ def _fixture_checks():
run_gate("--tools", "tools/stub_split.py", "--no-run", "--fail"), 0)
check("gate e2e: --fail with the run grades that same split (rc 1)",
run_gate("--tools", "tools/stub_split.py", "--fail"), 1)
# THE WRAPPED PAIR, END TO END. Three stubs whose only difference is
# whether a leg prints the unit the wrap names, so these cells grade the
# SPENDING rule and not just the reading rule above.
check("gate e2e: a wrapped claim a leg binds, agreeing, is rc 0",
run_gate("--tools", "tools/stub_wrap_ok.py", "--fail"), 0)
check("gate e2e: and its wrapped VALUE is graded -- disagreeing is rc 1",
run_gate("--tools", "tools/stub_wrap_stale.py", "--fail"), 1)
check("gate e2e: a wrapped pair no leg binds is not a claim (rc 0)",
run_gate("--tools", "tools/stub_wrap_unbound.py", "--fail"), 0)
check("gate e2e: --no-run spends no wrap and fails on none (rc 0)",
run_gate("--tools", "tools/stub_wrap_stale.py", "--no-run",
"--fail"), 0)
# THE FOLD: a plural verb must not be bound to a singular printed key by
# a wrap, and the same text on one line must still be graded.
check("gate e2e: a wrapped verb is NOT bound by a plural fold (rc 0)",
run_gate("--tools", "tools/stub_wrap_fold.py", "--fail"), 0)
check("gate e2e: the same words on one line ARE folded and graded (rc 1)",
run_gate("--tools", "tools/stub_note_adjacent.py", "--fail"), 1)
# the process exit code through the vocabulary, both directions, plus
# the two ways a vocabulary can fail to be usable at all
vpath = os.path.join(td, "vocab-names-unit.json")
@@ -1017,6 +1214,30 @@ def _fixture_checks():
if l.strip().startswith("ADVISORY above (no census label")],
["ADVISORY above (no census label and no cached vocabulary): "
"5@3, 7@5 -- reported, not graded"])
# A WRAP IS NEVER SILENT: the banner says how many pairs were read
# across a break and how many were spent, on both outcomes.
_rc, out_wb = run_gate_out("--tools", "tools/stub_wrap_ok.py")
check("gate e2e: the banner says the wrap was read and spent",
[l.strip() for l in out_wb.splitlines()
if l.strip().startswith("wrapped:")],
["wrapped: 1 pair(s) read across a line break, 1 spent, "
"0 not a claim (no leg prints the unit)"])
_rc, out_wu = run_gate_out("--tools", "tools/stub_wrap_unbound.py")
check("gate e2e: an unbound wrap is counted and named, not dropped",
[l.strip() for l in out_wu.splitlines()
if l.strip().startswith(("wrapped:", "WRAP_UNBOUND"))],
["wrapped: 1 pair(s) read across a line break, 0 spent, "
"1 not a claim (no leg prints the unit)",
"WRAP_UNBOUND tools/stub_wrap_unbound.py:3 0 without -- the "
"unit is printed by no leg"])
_rc, out_wf = run_gate_out("--tools", "tools/stub_wrap_fold.py")
check("gate e2e: a folded verb is named UNBOUND, not graded STALE",
[l.strip() for l in out_wf.splitlines()
if l.strip().startswith(("wrapped:", "WRAP_UNBOUND", "STALE"))],
["wrapped: 1 pair(s) read across a line break, 0 spent, "
"1 not a claim (no leg prints the unit)",
"WRAP_UNBOUND tools/stub_wrap_fold.py:3 62 notes -- the unit "
"is printed by no leg"])
print(f"selftest: {cells[0]} cells, {cells[0] - bad[0]} passed, {bad[0]} failed")
return 0 if ok else 1
@@ -1118,6 +1339,29 @@ def unit_scope(unit, legs, no_run):
return "NOT_A_CENSUS_UNIT"
def wrap_bound(unit, legs):
"""True when some leg prints this unit under its OWN spelling.
This is keys_for WITHOUT the plural fold, and the narrowing is the whole
point. `keys_for` tries `unit.lower().rstrip("s")` because a one-line claim
and a printed label are both hand-written census voice (`18 claims` vs
`claim: 18`), so folding the plural costs nothing there. A WRAP IS NOT
HAND-WRITTEN CENSUS VOICE -- it is two lines this reader hypothesised into
one claim, and the word that opens the second line is a word in a sentence
far more often than it is a label: `the queue's own reply 62 / notes it`
folds `notes` onto a printed key `note`, and the tool printed that key only
because `parse_printed`'s loose reader took it out of the prose of its own
PASS line (`PASS v2 loose key: a Q7 continuation note is not an opening
record`). Measured 2026-10-20: that fold spent the pair and graded the
docstring STALE 62-vs-7 -- a false fault about a correct sentence.
So a wrap binds on the unit's own spelling or not at all; a pair that only a
fold would bind is printed as WRAP_UNBOUND and never graded, which is the
same landing place as a pair no leg touches.
"""
u = unit.lower()
return any(u in (printed or {}) for printed, _clone, _tag in legs)
def vocab_has(unit, vocab):
"""True when a cached census vocabulary names this unit word.
@@ -1200,13 +1444,51 @@ def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240,
out.append((selftest_leg[0][0], selftest_leg[0][1], "selftest"))
return out
# A WRAPPED PAIR IS SPENT ONLY WHEN A LEG CAN BIND ITS UNIT. Joining a line
# break is a reading decision and it is the only place in this tool where a
# claim is manufactured out of two lines instead of found inside one, so it
# gets the tool's own test for census-hood rather than a lexicon of English:
# a unit some leg prints IS a unit; a word that merely opens a sentence is
# not, and the difference is measured on the tool's output rather than
# asserted in a word list. Measured at HEAD, spent: `1,062 CONTENT_SHA256`
# (citation_hash_class.py:53). Measured at HEAD, NOT spent and named in the
# banner: `69 filenames` (checkin_check.py:14, no leg prints `filenames`),
# `18 cells`, `24 spendable` and `0 without` (doc_census_check.py self), and
# `62 notes` (queue_claim_check.py:119, `reply 62 / notes it` -- prose, and
# only a plural fold would have bound it; see wrap_bound). Nothing is dropped
# silently -- every unbound pair is counted and printed (the `wrapped:` line
# in the banner), because a candidate that vanishes is the defect this rule
# exists to close. With no run there is no leg, so no wrap is spent and every
# one is reported unbound: a mode that cannot see a census cannot bind a
# claim, which is the same contract unit_scope already applies to a split.
wrap_candidates, wrap_unbound = 0, []
if any(w for _f, _r, cl, _b in blk_claims for _ln, _v, _u, _t, _p, w in cl):
kept_blocks = []
for first, revs, cl, btext in blk_claims:
rows = []
for ln, val, unit, tok, pos, wrapped in cl:
if not wrapped:
rows.append((ln, val, unit, tok, pos, wrapped))
continue
wrap_candidates += 1
bound = any(wrap_bound(unit, legs_for(first, r))
for r in (revs or ["HEAD"]))
if bound:
rows.append((ln, val, unit, tok, pos, wrapped))
else:
wrap_unbound.append({"file": name, "line": ln, "unit": unit,
"claim": val, "token": tok})
if rows:
kept_blocks.append((first, revs, rows, btext))
blk_claims = kept_blocks
# 1. INTERNAL SPLIT: one unit, one revision scope, two values, far enough
# apart to be a restatement rather than a neighbouring sub-count.
splits, advisory, unchecked = [], [], collections.Counter()
by_unit = collections.defaultdict(list)
for first, revs, cl, _b in blk_claims:
scope = tuple(revs)
for ln, val, unit, _t, pos in cl:
for ln, val, unit, _t, pos, _w in cl:
by_unit[(unit.upper() if unit.isupper() else unit.lower(), scope)].append(
(first, ln, val, pos))
for (unit, scope), items in sorted(by_unit.items()):
@@ -1261,7 +1543,7 @@ def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240,
for first, revs, cl, _b in blk_claims:
for rev in (revs or ["HEAD"]):
if is_self:
for ln, val, unit, tok, _pos in cl:
for ln, val, unit, tok, _pos, _w in cl:
verdicts.append({"file": name, "block": first, "line": ln,
"rev": rev, "unit": unit, "claim": val,
"token": tok, "verdict": "SELF_NOT_RUN",
@@ -1290,7 +1572,7 @@ def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240,
legs = [l for l in legs if l[2] == "selftest"]
if not legs:
continue
for ln, val, unit, tok, _pos in cl:
for ln, val, unit, tok, _pos, _w in cl:
v, detail = adjudicate(legs, unit, val, revs)
verdicts.append({"file": name, "block": first, "line": ln,
"rev": rev, "unit": unit, "claim": val,
@@ -1299,8 +1581,9 @@ def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240,
"unchecked": dict(unchecked),
"claim_units": sorted({(u.upper() if u.isupper() else u.lower())
for _f, _r, cl, _b in blk_claims
for _ln, _v, u, _t, _p in cl}),
for _ln, _v, u, _t, _p, _w in cl}),
"failures": failures, "verdicts": verdicts,
"wrap_candidates": wrap_candidates, "wrap_unbound": wrap_unbound,
"self": is_self and run, "selftest_leg": (not run) or bool(selftest_leg and selftest_leg[0]),
"blocks_with_claims": len(blk_claims)}
@@ -1461,6 +1744,14 @@ def main():
f"{nstale} STALE claim(s), {nfail} leg failure(s), "
f"{nclaims} claims checked"
+ (" [" + "; ".join(notes) + "]" if notes else ""))
if rep.get("wrap_candidates"):
print(f" wrapped: {rep['wrap_candidates']} pair(s) read across a "
f"line break, {rep['wrap_candidates'] - len(rep.get('wrap_unbound') or [])} "
f"spent, {len(rep.get('wrap_unbound') or [])} not a claim "
f"(no leg prints the unit)")
for u in rep.get("wrap_unbound") or []:
print(f" WRAP_UNBOUND {u['file']}:{u['line']} "
f"{u['token']} -- the unit is printed by no leg")
for s in rep["splits"]:
vals = ", ".join(f"{r['value']}@{r['line']}" for r in s["rows"])
scope = ",".join(s["revs"]) or "no rev named"