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:
|
The code means:
|
||||||
|
|
||||||
0 ran, and graded no fault (every claim adjudicated agrees)
|
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
|
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
|
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
|
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
|
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.
|
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
|
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
|
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
|
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
|
USAGE
|
||||||
python3 doc_census_check.py [--repo DIR] [--rev HEAD] [--tools NAME...]
|
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 argparse
|
||||||
import ast
|
import ast
|
||||||
@@ -806,6 +827,24 @@ def _fixture_checks():
|
|||||||
check("mode: --fail cannot fail on a split it could not verify",
|
check("mode: --fail cannot fail on a split it could not verify",
|
||||||
gate_rc([rep_norun], True), 0)
|
gate_rc([rep_norun], True), 0)
|
||||||
check("mode: and still fails on the verified one", gate_rc([rep_run], True), 1)
|
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:
|
with tempfile.TemporaryDirectory(prefix="doccensus-gate-") as td:
|
||||||
os.makedirs(os.path.join(td, "tools"))
|
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)
|
run_gate("--tools", "tools/stub_split.py", "--no-run", "--fail"), 0)
|
||||||
check("gate e2e: --fail with the run grades that same split (rc 1)",
|
check("gate e2e: --fail with the run grades that same split (rc 1)",
|
||||||
run_gate("--tools", "tools/stub_split.py", "--fail"), 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")
|
print(f"selftest: {cells[0]} cells, {cells[0] - bad[0]} passed, {bad[0]} failed")
|
||||||
return 0 if ok else 1
|
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
|
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.
|
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
|
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:
|
if no_run or not legs:
|
||||||
return ("LABEL_ONLY" if is_label(unit) else "UNCHECKED_NO_ORACLE")
|
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"
|
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()
|
text = open(script, encoding="utf-8", errors="replace").read()
|
||||||
span = docstring_span(script, text)
|
span = docstring_span(script, text)
|
||||||
if span is None:
|
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")
|
rev_for_oracle = (items[0][0], scope[0] if len(scope) == 1 else "HEAD")
|
||||||
why = ("SELF_NOT_RUN" if is_self and run
|
why = ("SELF_NOT_RUN" if is_self and run
|
||||||
else unit_scope(unit, legs_for(*rev_for_oracle), not 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),
|
row = {"unit": unit, "revs": list(scope), "values": sorted(vals),
|
||||||
"how": why, "rows": [{"line": ln, "value": v, "block": b}
|
"how": why, "oracle_rev": oracle_rev,
|
||||||
for b, ln, v, _ in items]}
|
"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
|
# 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
|
# printed its unit with exactly one value (CHECKABLE), or when a cached
|
||||||
# and the output are talking about one number and a disagreement is a
|
# vocabulary proves the unit is a census unit and no run happened
|
||||||
# defect the gate can verify. Every other reason -- no oracle at all
|
# (CACHE_CHECKABLE): then the docstring and the census are talking about
|
||||||
# (LABEL_ONLY, --no-run), a unit no leg prints (NOT_A_CENSUS_UNIT), a
|
# one number and a disagreement is a defect the gate can verify. Every
|
||||||
# unit the tool itself prints per section (SECTION_SPLIT_BY_TOOL), this
|
# other reason -- no oracle at all (LABEL_ONLY, --no-run), a unit no leg
|
||||||
# tool reading itself (SELF_NOT_RUN) -- is reported with its values and
|
# prints (NOT_A_CENSUS_UNIT), a unit the tool itself prints per section
|
||||||
# never reaches the gate. Decided 2026-10-18 (reply 22) after
|
# (SECTION_SPLIT_BY_TOOL), this tool reading itself (SELF_NOT_RUN) -- is
|
||||||
# `--fail --no-run` returned 1 on the 471/441 split at fca3f2e while the
|
# reported with its values and never reaches the gate. Decided 2026-10-18
|
||||||
# full run returned 0 for the same units it could not see: rc 1 must
|
# (reply 22) after `--fail --no-run` returned 1 on the 471/441 split at
|
||||||
# never mean "I could not check this".
|
# fca3f2e while the full run returned 0 for the same units it could not
|
||||||
if why != "CHECKABLE":
|
# see: rc 1 must never mean "I could not check this".
|
||||||
|
if why not in ("CHECKABLE", "CACHE_CHECKABLE"):
|
||||||
unchecked[why] += 1
|
unchecked[why] += 1
|
||||||
advisory.append(row)
|
advisory.append(row)
|
||||||
continue
|
continue
|
||||||
@@ -1157,12 +1259,40 @@ def main():
|
|||||||
"VERIFIED (CHECKABLE) or a STALE claim; unverified "
|
"VERIFIED (CHECKABLE) or a STALE claim; unverified "
|
||||||
"splits and leg failures are reported, never spent as "
|
"splits and leg failures are reported, never spent as "
|
||||||
"defects (rc 2 stays 'could not grade')")
|
"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")
|
ap.add_argument("--selftest", action="store_true")
|
||||||
a = ap.parse_args()
|
a = ap.parse_args()
|
||||||
|
|
||||||
if a.selftest:
|
if a.selftest:
|
||||||
return _fixture_checks()
|
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)
|
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}
|
tree = {p for p in store.decode("utf-8", "replace").split("\n") if p}
|
||||||
tools, missing = resolve_tools(a.tools, tree)
|
tools, missing = resolve_tools(a.tools, tree)
|
||||||
@@ -1186,7 +1316,8 @@ def main():
|
|||||||
fh.write(src)
|
fh.write(src)
|
||||||
try:
|
try:
|
||||||
rep = check_tool(a.repo, local, t, cache, run=not a.no_run,
|
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:
|
except subprocess.TimeoutExpired:
|
||||||
rep = {"tool": t, "splits": [], "blocks_with_claims": 0,
|
rep = {"tool": t, "splits": [], "blocks_with_claims": 0,
|
||||||
"verdicts": [{"file": t, "verdict": "TIMEOUT", "rev": "-",
|
"verdicts": [{"file": t, "verdict": "TIMEOUT", "rev": "-",
|
||||||
@@ -1196,6 +1327,10 @@ def main():
|
|||||||
|
|
||||||
print(f"tools scanned at {a.rev}: {len(reports)} "
|
print(f"tools scanned at {a.rev}: {len(reports)} "
|
||||||
f"({'internal-split only, label vocabulary' if a.no_run else 'docstring vs printed census'})")
|
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()
|
tot = collections.Counter()
|
||||||
for rep in reports:
|
for rep in reports:
|
||||||
bad = rep["failures"] + [v for v in rep["verdicts"] if v["verdict"] == "STALE"]
|
bad = rep["failures"] + [v for v in rep["verdicts"] if v["verdict"] == "STALE"]
|
||||||
@@ -1207,7 +1342,9 @@ def main():
|
|||||||
for s in rep["splits"]:
|
for s in rep["splits"]:
|
||||||
vals = ", ".join(f"{r['value']}@{r['line']}" for r in s["rows"])
|
vals = ", ".join(f"{r['value']}@{r['line']}" for r in s["rows"])
|
||||||
scope = ",".join(s["revs"]) or "no rev named"
|
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()):
|
for k, c in sorted(rep["unchecked"].items()):
|
||||||
print(f" (not flagged: {c} split(s) in units {k})")
|
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
|
# 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:
|
with open(a.json, "w") as fh:
|
||||||
json.dump({"rev": a.rev, "repo": a.repo, "reports": reports}, fh, indent=1)
|
json.dump({"rev": a.rev, "repo": a.repo, "reports": reports}, fh, indent=1)
|
||||||
print(f"wrote {a.json}")
|
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:
|
if a.fail:
|
||||||
nsplit, nstale = gate_faults(reports)
|
nsplit, nstale = gate_faults(reports)
|
||||||
if nsplit or nstale:
|
if nsplit or nstale:
|
||||||
print(f"\nFAIL: {nsplit} INTERNAL SPLIT + {nstale} STALE claim(s) "
|
print(f"\nFAIL: {nsplit} INTERNAL SPLIT + {nstale} STALE claim(s) "
|
||||||
f"at {a.rev}"
|
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
|
return 1
|
||||||
print(f"\nOK: 0 INTERNAL SPLIT, 0 STALE claim(s) at {a.rev}"
|
print(f"\nOK: 0 INTERNAL SPLIT, 0 STALE claim(s) at {a.rev}"
|
||||||
+ (" (no oracle in this mode: nothing was verified, so nothing is "
|
+ (" (no oracle in this mode: nothing was verified, so nothing is "
|
||||||
"graded -- ADVISORY rows above are labels, not faults)"
|
"graded -- ADVISORY rows above are labels, not faults"
|
||||||
if a.no_run else ""))
|
+ ("; 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)
|
return gate_rc(reports, a.fail)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user