volt: tool_map grades the tree it lives in, not the one it was written in
DEFAULT_REPO was the absolute /root/troupe-standup, so a copy's
`--selftest` -- the invocation selftest_sweep.py uses, no --repo, cwd =
the tree under sweep -- graded the home checkout and reported the copy
clean. Derived from __file__ instead: the default is the checkout the
tool lives in. Measured on a clone at c3058d5 with drifted docs: before,
repo-less --selftest 20/20 rc 0 vs --repo . 19/20 rc 1; after, 19/20
rc 1 both ways.
Also in this commit:
- verify.run_leg: one elapsed-ms clock (_elapsed_ms) and one except arm
for TimeoutExpired/OSError, instead of three copies of the arithmetic;
- write_probe c13: roster read from FLAG_PROBES rather than a hardcoded
three, and selftest_sweep.py --write-baseline added to the table;
- docs/TOOLS.md: the selftest_sweep row grows the flags it now prints.
This commit is contained in:
1 parent
2a1ef2feaf
commit
b859ad6464
4 files changed
+70
-36
No files matched your search
+1
-1
@@ -27,7 +27,7 @@ module docstring, flags from the tool's own `--help`, probe rows from
|
||||
| `queue_claim_check.py` | the claim check for `WORK-QUEUE.md`, shipped with its tests. | `--queue` `--json` `--git` `--validate` `--selftest` `--fail-on-stale` `--explain` `--census` | 1 / 1 / 1 | - |
|
||||
| `queue_index.py` | the work queue's table of contents, and the gate that it is current. | `--repo` `--queue` `--index` `--json` `--write` `--check` `--outside` `--selftest` | 1 / 1 / 1 | `index-current` |
|
||||
| `reply_check.py` | does every reply file's own header agree with its own name? | `--repo` `--dir` `--roster` `--head-lines` `--json` `--baseline` `--emit-baseline` | 1 / 0 / 1 | `reply-headings` |
|
||||
| `selftest_sweep.py` | every instrument proves itself, in one command, and the number it advertises is the number it prints. | `--repo` `--tools-dir` `--timeout` `--json` `--quiet` `--verbose` `--selftest` | 1 / 0 / 1 | `selftest-sweep` |
|
||||
| `selftest_sweep.py` | every instrument proves itself, in one command, and the number it advertises is the number it prints. | `--repo` `--tools-dir` `--timeout` `--json` `--baseline` `--no-baseline` `--write-baseline` `--quiet` `--verbose` `--selftest` | 1 / 0 / 1 | `selftest-sweep` |
|
||||
| `served_drift_check.py` | does the host actually serve the tree at this revision? | `--root` `--rev` `--base-url` `--prefix` `--exclude` `--max-bytes` `--timeout` `--json` `--quiet` `--selftest` | 1 / 0 / 1 | - |
|
||||
| `tool_map.py` | the instruments get a map, and the map is generated from the tree. | `--repo` `--rev` `--write` `--check` `--json` `--selftest` | 1 / 0 / 1 | - |
|
||||
| `verify.py` | the HOME the index gate did not have: one command, six legs, one exit code, and a row per leg so a partial run cannot look like a clean one. | `--repo` `--queue` `--index` `--checkin-day` `--fast` `--timeout` `--json` `--quiet` `--selftest` | 1 / 0 / 1 | - |
|
||||
|
||||
+21
-5
@@ -58,10 +58,26 @@ THE PRE-COMMIT BLIND SPOT, AND THE MODE THAT CLOSES IT
|
||||
The gate did not catch it either. `selftest_sweep.py --repo .` at `4602bd5`
|
||||
reads **21 tools, 21 OK, 0 fatal**, because the sweep runs each tool as
|
||||
`[python, path, "--selftest"]` with no `--repo` (`selftest_sweep.py:449`) and
|
||||
this tool's `DEFAULT_REPO` is an absolute path, so its selftest graded
|
||||
/root/troupe-standup -- a different, clean tree. That is a second defect, and
|
||||
it is NOT fixed here: the sweep's selftest axis can grade the wrong tree. It
|
||||
is recorded because it is why the pre-commit runs stayed green.
|
||||
this tool's `DEFAULT_REPO` was an absolute path, so its selftest graded
|
||||
/root/troupe-standup -- a different, clean tree. That was a second defect:
|
||||
the sweep's selftest axis could grade the wrong tree, and it did.
|
||||
|
||||
FIXED 2026-10-22 (volt). `DEFAULT_REPO` is now derived from this file's own
|
||||
location -- `dirname(dirname(abspath(__file__)))` -- so the default target is
|
||||
the checkout the tool lives in: the same path in place, a different path in a
|
||||
copy. Measured on a clone at `c3058d5`, whose `docs/TOOLS.md` is drifted
|
||||
(`--check` reads **rc 1, DRIFT at line 30**):
|
||||
|
||||
- before the fix, `--selftest` with no `--repo` -- the sweep's own
|
||||
invocation, `cwd` = the clone -- read **20/20, rc 0**: it graded
|
||||
/root/troupe-standup, the clean tree, and called the clone's drift clean;
|
||||
- the same clone under `--selftest --repo .` read **19/20, rc 1**, the one
|
||||
failure being the currency cell below;
|
||||
- after the fix, the repo-less run reads **19/20, rc 1** -- the same
|
||||
verdict as `--repo .`, because the default now names the copy.
|
||||
|
||||
`selftest_sweep.py` needed no edit: `cwd` was already the tree under sweep,
|
||||
so the fix alone is enough for the sweep's axis to grade what it walks.
|
||||
|
||||
`--rev WORKTREE` derives the map from the files on disk, discovered through
|
||||
`git ls-files -- tools/` -- the index, which is what a commit of tracked
|
||||
@@ -103,7 +119,7 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
DEFAULT_REPO = "/root/troupe-standup"
|
||||
DEFAULT_REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DEFAULT_DOC = "docs/TOOLS.md"
|
||||
HELP_TIMEOUT = 30
|
||||
|
||||
|
||||
+34
-23
@@ -361,6 +361,16 @@ def leg_argv(key, repo, queue, index, checkin_day=None):
|
||||
raise KeyError(key)
|
||||
|
||||
|
||||
def _elapsed_ms(started):
|
||||
"""Milliseconds elapsed since `started`; the integer clock every row carries.
|
||||
|
||||
Extracted so run_leg() does not recompute `int((time.time() - started) * 1000)`
|
||||
at each of its exit paths -- the DRY collapse that previously ran the same
|
||||
expression three times (timeout, OSError, normal verdict).
|
||||
"""
|
||||
return int((time.time() - started) * 1000)
|
||||
|
||||
|
||||
def run_leg(leg, repo, queue, index, timeout, checkin_day=None):
|
||||
"""One leg -> one row. rc 0/1/2 are graded; anything else is ungradable."""
|
||||
tool = os.path.join(repo, "tools", leg["tool"])
|
||||
@@ -384,30 +394,31 @@ def run_leg(leg, repo, queue, index, timeout, checkin_day=None):
|
||||
proc = subprocess.run(leg_argv(leg["key"], repo, queue, index, day),
|
||||
cwd=repo, capture_output=True, text=True,
|
||||
timeout=timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
row.update(verdict=UNGRADABLE, detail="TIMEOUT after %ss" % timeout)
|
||||
row["ms"] = int((time.time() - started) * 1000)
|
||||
return row
|
||||
except OSError as exc:
|
||||
row.update(verdict=UNGRADABLE, detail="could not run: %s" % exc)
|
||||
row["ms"] = int((time.time() - started) * 1000)
|
||||
return row
|
||||
|
||||
out = (proc.stdout or "") + (proc.stderr or "")
|
||||
row["rc"] = proc.returncode
|
||||
row["detail"] = pick_detail(out, summary=(proc.returncode == 0))
|
||||
if proc.returncode == 0:
|
||||
row["verdict"] = PASS
|
||||
elif proc.returncode == 1:
|
||||
row["verdict"] = FAULT
|
||||
elif proc.returncode == 2:
|
||||
row["verdict"] = UNGRADABLE
|
||||
except (subprocess.TimeoutExpired, OSError) as exc:
|
||||
# Both transport failures collapse to the same ungradable shape; only the
|
||||
# detail differs -- timeout names the deadline, OSError names the error.
|
||||
if isinstance(exc, subprocess.TimeoutExpired):
|
||||
detail = "TIMEOUT after %ss" % timeout
|
||||
else:
|
||||
detail = "could not run: %s" % exc
|
||||
row.update(verdict=UNGRADABLE, detail=detail)
|
||||
else:
|
||||
# rc 3 is retired in this repo (one meaning, two codes); any other code
|
||||
# is a tool that left the contract, and the run cannot grade through it.
|
||||
row["verdict"] = UNGRADABLE
|
||||
row["detail"] = "unexpected rc %d: %s" % (proc.returncode, row["detail"])
|
||||
row["ms"] = int((time.time() - started) * 1000)
|
||||
out = (proc.stdout or "") + (proc.stderr or "")
|
||||
row["rc"] = proc.returncode
|
||||
row["detail"] = pick_detail(out, summary=(proc.returncode == 0))
|
||||
if proc.returncode == 0:
|
||||
row["verdict"] = PASS
|
||||
elif proc.returncode == 1:
|
||||
row["verdict"] = FAULT
|
||||
elif proc.returncode == 2:
|
||||
row["verdict"] = UNGRADABLE
|
||||
else:
|
||||
# rc 3 is retired in this repo (one meaning, two codes); any other
|
||||
# code is a tool that left the contract, and the run cannot grade
|
||||
# through it.
|
||||
row["verdict"] = UNGRADABLE
|
||||
row["detail"] = "unexpected rc %d: %s" % (proc.returncode, row["detail"])
|
||||
row["ms"] = _elapsed_ms(started)
|
||||
return row
|
||||
|
||||
|
||||
|
||||
+14
-7
@@ -191,6 +191,9 @@ FLAG_PROBES = {
|
||||
"queue_index.py": ("--write",
|
||||
["--repo", "{tinyrepo}", "--queue", "{queue}",
|
||||
"--write", "{flagtarget}"]),
|
||||
"selftest_sweep.py": ("--write-baseline",
|
||||
["--repo", "{tinyrepo}", "--write-baseline",
|
||||
"{flagtarget}"]),
|
||||
"tool_map.py": ("--write",
|
||||
["--repo", "{tinyrepo}", "--write", "{flagtarget}"]),
|
||||
}
|
||||
@@ -489,25 +492,29 @@ def selftest(repo):
|
||||
"covered=%s/%s uncovered=%s/%s outside-class=%s/%s"
|
||||
% (probe_ok, faults_ok, probe_un, faults_un, probe_no, faults_no))
|
||||
|
||||
# c13 is the integration cell: this repo's three caller-path write flags are
|
||||
# probed by the same code path main() uses, over the same fixtures.
|
||||
# c13 is the integration cell: every caller-path write flag this repo's
|
||||
# table names is probed by the same code path main() uses, over the same
|
||||
# fixtures. The roster is READ FROM FLAG_PROBES, not listed here: a cell
|
||||
# with its own hardcoded three would keep passing while the table grew,
|
||||
# which is the same blind spot the UNAXED rule closes one level down.
|
||||
real_rows = []
|
||||
with tempfile.TemporaryDirectory(prefix="write-selftest-flag-") as tdf:
|
||||
fxf = build_fixtures(tdf, repo)
|
||||
for nm in ("doc_census_check.py", "queue_index.py", "tool_map.py"):
|
||||
for nm in sorted(FLAG_PROBES):
|
||||
pf = os.path.join(repo, "tools", nm)
|
||||
try:
|
||||
with open(pf, encoding="utf-8") as fh:
|
||||
s = fh.read()
|
||||
except OSError as exc:
|
||||
cell("c13 this repo's three caller-path write flags probe OK", False,
|
||||
"unreadable %s: %s" % (pf, exc))
|
||||
cell("c13 every caller-path write flag in the table probes OK",
|
||||
False, "unreadable %s: %s" % (pf, exc))
|
||||
real_rows = None
|
||||
break
|
||||
real_rows.extend(probe_flags(nm, s, pf, fxf, 300, fxf["tinyrepo"]))
|
||||
if real_rows is not None:
|
||||
cell("c13 this repo's three caller-path write flags probe OK",
|
||||
len(real_rows) == 3 and all(r["verdict"] == "OK" for r in real_rows),
|
||||
cell("c13 every caller-path write flag in the table probes OK",
|
||||
len(real_rows) == len(FLAG_PROBES)
|
||||
and all(r["verdict"] == "OK" for r in real_rows),
|
||||
str([(r["tool"], r["flag"], r["verdict"]) for r in real_rows]))
|
||||
|
||||
# c14/c15 are the blind spot the axis exists for, measured on a fixture tool
|
||||
|
||||
Reference in new issue
Block a user