tools (volt): doc_census_check -- a cached vocabulary gives the fast mode its teeth back
Reply 22 ruled the asymmetry a defect and asked that `--fail` never return 1 on a split it could not verify. 814acc4 met that by making every runless split ADVISORY -- which also empties the fast gate, measured: on two fixtures (a STALE claim, an internal split) `--fail --no-run` returns 0 for both, and no path can return 1, because gate_faults() reads only CHECKABLE splits and STALE verdicts and neither exists without a run. So the fast mode gets the oracle it can have. `--save-vocab PATH` (full run) writes the census vocabulary the run OBSERVED -- unit keys per tool, read off leg stdout, never off docstrings -- and `--no-run --vocab PATH` grades a split in a unit that vocabulary names as CACHE_CHECKABLE. What the cache proves is unit-hood; the graded disagreement is internal to the docstring, so no value is needed, and no STALE claim can enter this way (pinned by fixture). Measured atfca3f2e, tools/citation_resolve_check.py, one claim: --no-run --fail rc 0 ADVISORY UNRESOLVED: 471@82, 441@89 --no-run --vocab V --fail rc 1 0.205s, SPLIT ... (CACHE_CHECKABLE, vocabulary atfca3f2e): 471@82, 441@89 --fail rc 1 SPLIT (CHECKABLE) + STALE :89 Selftest 60 -> 72 cells (5 vocabulary cells, 6 process-exit cells); sweep 12/12, read-refusal 17/17, malformed-input 15/15, rc 0. rc 2 covers an unreadable --vocab and --save-vocab without a run.
This commit is contained in:
1 parent
fcfb323180
commit
ecdaecd7cb
1 file changed
+179
-23
+179
-23
@@ -152,9 +152,12 @@ FAILING ON THE CLASS IT CATCHES (`--fail`)
|
||||
The code means:
|
||||
|
||||
0 ran, and graded no fault (every claim adjudicated agrees)
|
||||
1 ran, and graded a fault (a VERIFIED INTERNAL SPLIT, or STALE)
|
||||
1 ran, and graded a fault (an INTERNAL SPLIT whose unit a leg --
|
||||
or a cached vocabulary -- verified,
|
||||
or a STALE claim)
|
||||
2 could not grade (usage, a --tools filter that matches
|
||||
nothing tracked, unreadable input)
|
||||
nothing tracked, an unreadable
|
||||
--vocab, --save-vocab without a run)
|
||||
|
||||
THE TWO MODES GRADE THE SAME CLASS. `--no-run` runs nothing, so it has no
|
||||
oracle and cannot prove any split is a census unit: it prints every split as
|
||||
@@ -167,6 +170,23 @@ FAILING ON THE CLASS IT CATCHES (`--fail`)
|
||||
run prints `UNRESOLVED 471` -- plus the STALE claim at :89. The teeth are
|
||||
kept where they can be verified, which is the only place they mean anything.
|
||||
|
||||
A CACHED VOCABULARY PUTS THE TEETH BACK IN THE FAST MODE, without asking it
|
||||
to invent an oracle. `--save-vocab PATH` (full run) writes the census
|
||||
vocabulary the run OBSERVED -- every unit key some leg printed, per tool --
|
||||
and `--no-run --vocab PATH` grades a split in a unit that vocabulary names as
|
||||
CACHE_CHECKABLE. What the cache proves is narrow and enough: the unit is a
|
||||
census unit for that tool (a leg printed it at `<vocab rev>`), and the
|
||||
disagreement being graded is INTERNAL to the docstring, so the leg never has
|
||||
to supply the value. What it cannot prove is a value, so a STALE claim is
|
||||
still impossible without a run, and a unit the vocabulary does not name stays
|
||||
ADVISORY -- the 39/12/1 `ACS-only` class is still never spent. Measured
|
||||
2026-10-18 at 814acc4 on a stub whose docstring splits 471/441 over a census
|
||||
printing 471: `--no-run --fail` rc 0, `--no-run --vocab V --fail` rc 1,
|
||||
`--fail` rc 1. The vocabulary's rev is printed with every CACHE_CHECKABLE
|
||||
row, because a reader is entitled to know how old the census behind the
|
||||
label is; it is NOT required to equal the revision being checked (unit-hood
|
||||
is a property of the unit word, and the graded disagreement is internal).
|
||||
|
||||
rc 2 keeps its meaning from the tools/ contract (6ee8fce) and is decided
|
||||
before the gate runs, so "could not grade" never reads as "graded clean" or
|
||||
as "graded a fault". A LEG FAILURE (RUN_FAILED) is reported in its own column
|
||||
@@ -176,7 +196,8 @@ FAILING ON THE CLASS IT CATCHES (`--fail`)
|
||||
|
||||
USAGE
|
||||
python3 doc_census_check.py [--repo DIR] [--rev HEAD] [--tools NAME...]
|
||||
[--json OUT] [--no-run] [--fail] [--selftest] # selftest: 60 cells, one per claim class
|
||||
[--json OUT] [--fail] [--no-run] [--vocab PATH] [--save-vocab PATH]
|
||||
[--selftest] # selftest: 72 cells, one per claim class
|
||||
"""
|
||||
import argparse
|
||||
import ast
|
||||
@@ -806,6 +827,24 @@ def _fixture_checks():
|
||||
check("mode: --fail cannot fail on a split it could not verify",
|
||||
gate_rc([rep_norun], True), 0)
|
||||
check("mode: and still fails on the verified one", gate_rc([rep_run], True), 1)
|
||||
# a cached vocabulary grades unit-hood without a run -- and only when it
|
||||
# names the unit, which is the whole difference between a proof and a
|
||||
# guess (reply 22, option 2)
|
||||
check("vocab: a cached census unit is recognised",
|
||||
vocab_has("UNRESOLVED", {"unresolved", "citations"}), True)
|
||||
check("vocab: a unit the vocabulary does not name is not",
|
||||
vocab_has("WOMBATS", {"unresolved"}), False)
|
||||
rep_cached = check_tool(td, split_stub, "stub_split.py", {}, run=False,
|
||||
tmpdir=td, vocab={"unresolved"}, vocab_rev="abc1234")
|
||||
check("vocab: the same runless split grades as CACHE_CHECKABLE",
|
||||
[(s["unit"], s["how"], s["oracle_rev"]) for s in rep_cached["splits"]],
|
||||
[("UNRESOLVED", "CACHE_CHECKABLE", "abc1234")])
|
||||
check("vocab: and the gate spends it", gate_rc([rep_cached], True), 1)
|
||||
rep_other = check_tool(td, split_stub, "stub_split.py", {}, run=False,
|
||||
tmpdir=td, vocab={"wombats"}, vocab_rev="abc1234")
|
||||
check("vocab: a vocabulary naming another unit leaves it advisory",
|
||||
([s["unit"] for s in rep_other["splits"]], gate_rc([rep_other], True)),
|
||||
([], 0))
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="doccensus-gate-") as td:
|
||||
os.makedirs(os.path.join(td, "tools"))
|
||||
@@ -841,6 +880,41 @@ 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 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")
|
||||
with open(vpath, "w") as fh:
|
||||
json.dump({"rev": "abc1234",
|
||||
"tools": {"tools/stub_split.py": ["unresolved"]}}, fh)
|
||||
check("gate e2e: --no-run --vocab grades a split the vocabulary names (rc 1)",
|
||||
run_gate("--tools", "tools/stub_split.py", "--no-run",
|
||||
"--vocab", vpath, "--fail"), 1)
|
||||
vother = os.path.join(td, "vocab-other-unit.json")
|
||||
with open(vother, "w") as fh:
|
||||
json.dump({"rev": "abc1234",
|
||||
"tools": {"tools/stub_split.py": ["wombats"]}}, fh)
|
||||
check("gate e2e: a vocabulary that does not name the unit leaves rc 0",
|
||||
run_gate("--tools", "tools/stub_split.py", "--no-run",
|
||||
"--vocab", vother, "--fail"), 0)
|
||||
check("gate e2e: an unreadable --vocab is rc 2, not 0 and not 1",
|
||||
run_gate("--tools", "tools/stub_split.py", "--no-run",
|
||||
"--vocab", os.path.join(td, "nope.json"), "--fail"), 2)
|
||||
check("gate e2e: --save-vocab without a run is rc 2",
|
||||
run_gate("--tools", "tools/stub_split.py", "--no-run",
|
||||
"--save-vocab", os.path.join(td, "v0.json"), "--fail"), 2)
|
||||
# the round trip a gate runner would actually run: a full run writes the
|
||||
# vocabulary it observed, the fast mode reads it back and still fails
|
||||
vrt = os.path.join(td, "vocab-roundtrip.json")
|
||||
check("gate e2e: a full run writes the vocabulary it observed (rc 1)",
|
||||
run_gate("--tools", "tools/stub_split.py", "--save-vocab", vrt,
|
||||
"--fail"), 1)
|
||||
with open(vrt) as fh:
|
||||
check("gate e2e: the written vocabulary names the unit the leg printed",
|
||||
sorted(json.load(fh)["tools"].get("tools/stub_split.py", [])),
|
||||
["unresolved"])
|
||||
check("gate e2e: the round trip fails the fast mode on the same split",
|
||||
run_gate("--tools", "tools/stub_split.py", "--no-run",
|
||||
"--vocab", vrt, "--fail"), 1)
|
||||
|
||||
print(f"selftest: {cells[0]} cells, {cells[0] - bad[0]} passed, {bad[0]} failed")
|
||||
return 0 if ok else 1
|
||||
@@ -920,7 +994,9 @@ def unit_scope(unit, legs, no_run):
|
||||
DESIGN (`citations` prints once under EXPLICIT and once under BARE), so a
|
||||
docstring that mirrors both is correct and is reported as such, not flagged.
|
||||
A unit no leg prints is not a census unit here. Without a run, only
|
||||
uppercase census labels are checked.
|
||||
uppercase census labels are checked -- and a cached vocabulary (see
|
||||
vocab_has) can widen that, which is why the cache decision is taken in
|
||||
check_tool, not here: this function answers about the legs it was given.
|
||||
"""
|
||||
if no_run or not legs:
|
||||
return ("LABEL_ONLY" if is_label(unit) else "UNCHECKED_NO_ORACLE")
|
||||
@@ -940,7 +1016,19 @@ def unit_scope(unit, legs, no_run):
|
||||
return "NOT_A_CENSUS_UNIT"
|
||||
|
||||
|
||||
def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240):
|
||||
def vocab_has(unit, vocab):
|
||||
"""True when a cached census vocabulary names this unit word.
|
||||
|
||||
The vocabulary is a set of the unit KEYS a full run observed (`unresolved`,
|
||||
`citations`, `acs_only` ...), and `keys_for` is the same matcher the oracle
|
||||
uses -- so a cache hit means exactly "a leg printed this unit under this
|
||||
key", no letter of the rule re-invented for the cache.
|
||||
"""
|
||||
return bool(keys_for(unit, {u: [0] for u in (vocab or ())}))
|
||||
|
||||
|
||||
def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240,
|
||||
vocab=None, vocab_rev=None):
|
||||
text = open(script, encoding="utf-8", errors="replace").read()
|
||||
span = docstring_span(script, text)
|
||||
if span is None:
|
||||
@@ -1015,21 +1103,35 @@ def check_tool(repo, script, name, cache, run=True, tmpdir=".", isolation=240):
|
||||
rev_for_oracle = (items[0][0], scope[0] if len(scope) == 1 else "HEAD")
|
||||
why = ("SELF_NOT_RUN" if is_self and run
|
||||
else unit_scope(unit, legs_for(*rev_for_oracle), not run))
|
||||
oracle_rev = None
|
||||
# A CACHED VOCABULARY WIDENS WHAT A RUNLESS MODE MAY GRADE, and only in
|
||||
# the one direction it can prove: a unit some leg printed at the
|
||||
# vocabulary's rev is a census unit, and the disagreement here is
|
||||
# internal to this docstring, so the leg's VALUE is never needed. It is
|
||||
# consulted only when nothing ran -- with a run, a unit the live leg
|
||||
# does not print is evidence AGAINST census-hood and the cache must not
|
||||
# overrule it. A STALE claim never enters through here: `why` only
|
||||
# decides splits.
|
||||
if why in ("LABEL_ONLY", "UNCHECKED_NO_ORACLE") and not run \
|
||||
and vocab_has(unit, vocab):
|
||||
why, oracle_rev = "CACHE_CHECKABLE", vocab_rev
|
||||
row = {"unit": unit, "revs": list(scope), "values": sorted(vals),
|
||||
"how": why, "rows": [{"line": ln, "value": v, "block": b}
|
||||
for b, ln, v, _ in items]}
|
||||
"how": why, "oracle_rev": oracle_rev,
|
||||
"rows": [{"line": ln, "value": v, "block": b}
|
||||
for b, ln, v, _ in items]}
|
||||
# THE ONLY DOOR INTO `splits`. A split is graded only when a leg ran and
|
||||
# printed its unit with exactly one value (CHECKABLE): then the docstring
|
||||
# and the output are talking about one number and a disagreement is a
|
||||
# defect the gate can verify. Every other reason -- no oracle at all
|
||||
# (LABEL_ONLY, --no-run), a unit no leg prints (NOT_A_CENSUS_UNIT), a
|
||||
# unit the tool itself prints per section (SECTION_SPLIT_BY_TOOL), this
|
||||
# tool reading itself (SELF_NOT_RUN) -- is reported with its values and
|
||||
# never reaches the gate. Decided 2026-10-18 (reply 22) after
|
||||
# `--fail --no-run` returned 1 on the 471/441 split at fca3f2e while the
|
||||
# full run returned 0 for the same units it could not see: rc 1 must
|
||||
# never mean "I could not check this".
|
||||
if why != "CHECKABLE":
|
||||
# printed its unit with exactly one value (CHECKABLE), or when a cached
|
||||
# vocabulary proves the unit is a census unit and no run happened
|
||||
# (CACHE_CHECKABLE): then the docstring and the census are talking about
|
||||
# one number and a disagreement is a defect the gate can verify. Every
|
||||
# other reason -- no oracle at all (LABEL_ONLY, --no-run), a unit no leg
|
||||
# prints (NOT_A_CENSUS_UNIT), a unit the tool itself prints per section
|
||||
# (SECTION_SPLIT_BY_TOOL), this tool reading itself (SELF_NOT_RUN) -- is
|
||||
# reported with its values and never reaches the gate. Decided 2026-10-18
|
||||
# (reply 22) after `--fail --no-run` returned 1 on the 471/441 split at
|
||||
# fca3f2e while the full run returned 0 for the same units it could not
|
||||
# see: rc 1 must never mean "I could not check this".
|
||||
if why not in ("CHECKABLE", "CACHE_CHECKABLE"):
|
||||
unchecked[why] += 1
|
||||
advisory.append(row)
|
||||
continue
|
||||
@@ -1157,12 +1259,40 @@ def main():
|
||||
"VERIFIED (CHECKABLE) or a STALE claim; unverified "
|
||||
"splits and leg failures are reported, never spent as "
|
||||
"defects (rc 2 stays 'could not grade')")
|
||||
ap.add_argument("--vocab", metavar="PATH",
|
||||
help="a census vocabulary written by --save-vocab: under "
|
||||
"--no-run, a split in a unit the vocabulary names is "
|
||||
"GRADED (CACHE_CHECKABLE) instead of labelled. The "
|
||||
"vocabulary proves unit-hood, never a value, so no "
|
||||
"STALE claim can enter this way")
|
||||
ap.add_argument("--save-vocab", metavar="PATH",
|
||||
help="write the census vocabulary THIS RUN observed (unit "
|
||||
"keys per tool) for a later --no-run --vocab; needs "
|
||||
"the run, so it is refused with --no-run (rc 2)")
|
||||
ap.add_argument("--selftest", action="store_true")
|
||||
a = ap.parse_args()
|
||||
|
||||
if a.selftest:
|
||||
return _fixture_checks()
|
||||
|
||||
if a.save_vocab and a.no_run:
|
||||
print("cannot save a vocabulary: --save-vocab needs the census a run "
|
||||
"prints, and --no-run runs nothing", file=sys.stderr)
|
||||
return 2
|
||||
vocab, vocab_rev = {}, None
|
||||
if a.vocab:
|
||||
try:
|
||||
with open(a.vocab, encoding="utf-8") as fh:
|
||||
vdoc = json.load(fh)
|
||||
vocab = {t: set(u) for t, u in (vdoc.get("tools") or {}).items()}
|
||||
vocab_rev = vdoc.get("rev")
|
||||
except (OSError, ValueError, AttributeError, TypeError) as exc:
|
||||
print(f"cannot grade: --vocab {a.vocab}: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
if not a.no_run:
|
||||
print("note: --vocab is read only by the runless mode; a live leg is "
|
||||
"the oracle when the tool runs", file=sys.stderr)
|
||||
|
||||
store = git(a.repo, "ls-tree", "-r", "--name-only", a.rev)
|
||||
tree = {p for p in store.decode("utf-8", "replace").split("\n") if p}
|
||||
tools, missing = resolve_tools(a.tools, tree)
|
||||
@@ -1186,7 +1316,8 @@ def main():
|
||||
fh.write(src)
|
||||
try:
|
||||
rep = check_tool(a.repo, local, t, cache, run=not a.no_run,
|
||||
tmpdir=tmpdir)
|
||||
tmpdir=tmpdir, vocab=vocab.get(t),
|
||||
vocab_rev=vocab_rev)
|
||||
except subprocess.TimeoutExpired:
|
||||
rep = {"tool": t, "splits": [], "blocks_with_claims": 0,
|
||||
"verdicts": [{"file": t, "verdict": "TIMEOUT", "rev": "-",
|
||||
@@ -1196,6 +1327,10 @@ def main():
|
||||
|
||||
print(f"tools scanned at {a.rev}: {len(reports)} "
|
||||
f"({'internal-split only, label vocabulary' if a.no_run else 'docstring vs printed census'})")
|
||||
if a.vocab:
|
||||
print(f"vocabulary: {a.vocab} -- census at {vocab_rev}, "
|
||||
f"{len(vocab)} tool(s), {sum(len(v) for v in vocab.values())} unit(s)"
|
||||
+ ("" if a.no_run else " (not read: a live leg is the oracle)"))
|
||||
tot = collections.Counter()
|
||||
for rep in reports:
|
||||
bad = rep["failures"] + [v for v in rep["verdicts"] if v["verdict"] == "STALE"]
|
||||
@@ -1207,7 +1342,9 @@ def main():
|
||||
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"
|
||||
print(f" SPLIT {s['unit']} [{scope}] ({s['how']}): {vals}")
|
||||
prov = (f", vocabulary at {s['oracle_rev']}"
|
||||
if s.get("oracle_rev") else "")
|
||||
print(f" SPLIT {s['unit']} [{scope}] ({s['how']}{prov}): {vals}")
|
||||
for k, c in sorted(rep["unchecked"].items()):
|
||||
print(f" (not flagged: {c} split(s) in units {k})")
|
||||
# A --no-run split is the one advisory class a reader of the fast mode
|
||||
@@ -1235,17 +1372,36 @@ def main():
|
||||
with open(a.json, "w") as fh:
|
||||
json.dump({"rev": a.rev, "repo": a.repo, "reports": reports}, fh, indent=1)
|
||||
print(f"wrote {a.json}")
|
||||
if a.save_vocab:
|
||||
# The vocabulary is READ OFF THE RUN, not off the docstrings: only the
|
||||
# keys `parse_printed` saw in a leg's stdout count as census units, so a
|
||||
# tool whose leg failed contributes nothing and cannot hand the fast
|
||||
# mode a vocabulary it never printed.
|
||||
units = collections.defaultdict(set)
|
||||
for (name, rev), entry in cache.items():
|
||||
if rev == "<selftest>" or not entry[0]:
|
||||
continue
|
||||
units[name].update(entry[0][0].keys())
|
||||
with open(a.save_vocab, "w", encoding="utf-8") as fh:
|
||||
json.dump({"rev": a.rev, "repo": a.repo,
|
||||
"tools": {k: sorted(v) for k, v in units.items()}},
|
||||
fh, indent=1, sort_keys=True)
|
||||
print(f"wrote vocabulary {a.save_vocab}: {len(units)} tool(s), "
|
||||
f"{sum(len(v) for v in units.values())} unit(s) at {a.rev}")
|
||||
if a.fail:
|
||||
nsplit, nstale = gate_faults(reports)
|
||||
if nsplit or nstale:
|
||||
print(f"\nFAIL: {nsplit} INTERNAL SPLIT + {nstale} STALE claim(s) "
|
||||
f"at {a.rev}"
|
||||
+ (" (internal-split only: --no-run)" if a.no_run else ""))
|
||||
+ (" (graded on a cached census vocabulary: --no-run --vocab)"
|
||||
if a.no_run else ""))
|
||||
return 1
|
||||
print(f"\nOK: 0 INTERNAL SPLIT, 0 STALE claim(s) at {a.rev}"
|
||||
+ (" (no oracle in this mode: nothing was verified, so nothing is "
|
||||
"graded -- ADVISORY rows above are labels, not faults)"
|
||||
if a.no_run else ""))
|
||||
"graded -- ADVISORY rows above are labels, not faults"
|
||||
+ ("; the vocabulary graded any split in a unit it names)"
|
||||
if a.vocab else ")")
|
||||
if a.no_run else ""))
|
||||
return gate_rc(reports, a.fail)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user