sol: Q74 -- the daily-ledger triage CHECK grades the commit it ships in (rc 0, selftest 10/10)
The CHECK resolved its artifact from BASE = /root/.hermes/wizard-outputs (module
scope), so it graded the host copy. Measured at c74e8af from a git archive export
with no .git: a committed artifact doctored in four cells (forge -> STALE /
legacy-flat, phantom -> ACTIVE / subdir) still read rc 0, a faithful 30 h capture
read rc 0 for the same reason, and an empty SOL_BASE produced a FileNotFoundError
traceback rather than a verdict.
Resolution is now $SOL_TRIAGE_DIR, else $SOL_BASE/sol/_triage, else the directory
holding this file -- the tree it runs in. Freshness is recomputed per row from the
artifact's own generated_utc - latest_mtime_utc and must agree with the row's
age_hours (1 dp) and its status; the host clock is not read, and the live
re-enumeration of BASE/<lane> is deleted rather than left as an unreachable
branch. Exit codes: 0 clean, 1 graded fault, 2 could not grade.
Readings, each its own `git archive c74e8af` tree plus this file, no .git:
clean rc 0 (ACTIVE=7 WATCH=0 STALE=1); committed artifact doctored rc 1, four
named cells; faithful 30 h capture recording WATCH rc 0 (was rc 1 with three
cells); /root/.hermes masked to 0 entries rc 0; missing SOL_TRIAGE_DIR rc 2.
--selftest: 10 cells, 10 passed, 0 failed under python 3.14.7 and 3.12.3.
The lane file's own rc is 0 in-tree and from the export. Evidence:
wizard-work/volt/q74-2026-10-26.md
This commit is contained in:
1 parent
c74e8af937
commit
13b77ba9d2
2 files changed
+463
-107
No files matched your search
@@ -1,146 +1,323 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Independent CHECK for the corrected sol-lane daily-ledger triage artifact.
|
"""CHECK for the sol-lane daily-ledger triage artifact (Q74).
|
||||||
|
|
||||||
This is a real test, not a tautology: it re-enumerates BOTH filesystem surfaces
|
Grades the artifact *committed beside this file* against **its own recorded
|
||||||
(legacy flat BASE/<lane>-*.txt AND subdir BASE/<lane>/*) with its own logic and
|
mtimes**. It never asks the host clock and never reads the live lane
|
||||||
recomputes the classification from mtimes, then compares against the recorded
|
directories, so a `git archive` export with no `.git` and no host state grades
|
||||||
artifact and against the committed filename.
|
the commit rather than the machine it runs on.
|
||||||
|
|
||||||
Exit 0 = the corrected artifact is correct. Nonzero = the artifact is wrong.
|
Artifact resolution, in order:
|
||||||
|
1. $SOL_TRIAGE_DIR (explicit override)
|
||||||
|
2. $SOL_BASE/sol/_triage (base override, old shape)
|
||||||
|
3. the directory holding this file (the tree it runs in -- the default)
|
||||||
|
|
||||||
|
Before Q74 the default was (2) with $SOL_BASE hardcoded to this host's
|
||||||
|
`/root/.hermes/wizard-outputs`, so the committed artifact was only read when the
|
||||||
|
host copy happened to be absent, and the freshness assertions were readings of
|
||||||
|
the host clock: a faithful capture of a 30 h-idle lane went red.
|
||||||
|
|
||||||
|
What replaced the freshness assertions: each row's `age_hours` and `status` are
|
||||||
|
recomputed from `generated_utc - latest_mtime_utc`, i.e. from the capture's own
|
||||||
|
two timestamps, and must agree with what the row records. A row that disagrees
|
||||||
|
with itself fails; a row that faithfully records 30 h idle passes. The live
|
||||||
|
re-enumeration of `BASE/<lane>` is deleted rather than kept as an unreachable
|
||||||
|
branch -- that is the generator's job (`triage_gen_daily.py`), not the gate's.
|
||||||
|
|
||||||
|
Exit codes (the repo's tools/ contract): 0 clean, 1 graded fault, 2 could not
|
||||||
|
grade (no artifact, unreadable JSON, no rows).
|
||||||
|
|
||||||
Checks:
|
Checks:
|
||||||
1. generated_utc matches the run_date which matches the committed filename.
|
1. run_date, the committed filename's date and generated_utc's date agree.
|
||||||
2. All seven primary lanes read ACTIVE (newest artefact lives in BASE/<lane>/).
|
2. The lane id set is exactly the seven primary lanes plus the negative one.
|
||||||
3. Every primary lane names its surface as "subdir" and its count > 0.
|
3. Every primary row: surface "subdir", latest_log present, daily_log_count>0.
|
||||||
4. Negative control "phantom" (empty on both surfaces) reads STALE.
|
4. Every row: age_hours == generated_utc - latest_mtime_utc (1 dp), and
|
||||||
5. Artifact self-consistency: counts sum to lane rows.
|
status == AGE_CLASSIFY(that age). mtime after the capture is a fault.
|
||||||
|
5. The negative-control row: surface "none", no log, count 0, status STALE.
|
||||||
|
|
||||||
|
`--selftest` runs the cells below against fixture artifacts in a temp dir,
|
||||||
|
including the two mutants Q58 measured: a doctored committed artifact (must now
|
||||||
|
fail) and a faithful 30 h-old capture (must now pass).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
# Base is overridable for runnability from a repo checkout; defaults to the
|
# Directory holding this file: the tree it runs in.
|
||||||
# sol profile's live output root.
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
BASE = os.environ.get("SOL_BASE", "/root/.hermes/wizard-outputs")
|
|
||||||
# The generator writes the corrected artifact into a dedicated directory,
|
|
||||||
# separate from the raw logs each lane writes into BASE/<lane>/.
|
|
||||||
SOL_DIR = os.path.join(BASE, "sol", "_triage")
|
|
||||||
PRIMARY = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
|
PRIMARY = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
|
||||||
NEGATIVE = "phantom"
|
NEGATIVE = "phantom"
|
||||||
|
STAMP = "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
NAME_RE = re.compile(r"^daily-ledger-triage-(\d{4}-\d{2}-\d{2})\.json$")
|
||||||
|
# age_hours is recorded to one decimal place, so a faithful row can differ from
|
||||||
|
# the recomputed value by up to 0.05 h.
|
||||||
|
AGE_TOL = 0.06
|
||||||
|
|
||||||
|
|
||||||
def to_utc(ts):
|
def artifact_dir():
|
||||||
return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
"""The directory to grade, in the order documented above."""
|
||||||
|
if os.environ.get("SOL_TRIAGE_DIR"):
|
||||||
|
return os.environ["SOL_TRIAGE_DIR"]
|
||||||
|
base = os.environ.get("SOL_BASE")
|
||||||
|
if base:
|
||||||
|
return os.path.join(base, "sol", "_triage")
|
||||||
|
return HERE
|
||||||
|
|
||||||
|
|
||||||
def enumerate(pid):
|
def classify(age_hours):
|
||||||
"""Return the newest path + mtime across both surfaces, or None."""
|
"""AGE_CLASSIFY: ACTIVE <= 24 h, WATCH <= 168 h, else STALE."""
|
||||||
cands = []
|
if age_hours <= 24.0:
|
||||||
flat = os.path.join(BASE, pid + "-*.txt")
|
|
||||||
for p in __import__("glob").glob(flat):
|
|
||||||
cands.append(p)
|
|
||||||
subdir = os.path.join(BASE, pid)
|
|
||||||
if os.path.isdir(subdir):
|
|
||||||
for f in os.listdir(subdir):
|
|
||||||
full = os.path.join(subdir, f)
|
|
||||||
if os.path.isfile(full):
|
|
||||||
cands.append(full)
|
|
||||||
if not cands:
|
|
||||||
return None, None
|
|
||||||
latest = max(cands, key=os.path.getmtime)
|
|
||||||
return latest, os.path.getmtime(latest)
|
|
||||||
|
|
||||||
|
|
||||||
def classify(mtime_ts):
|
|
||||||
age_sec = (datetime.now(timezone.utc) - datetime.fromtimestamp(
|
|
||||||
mtime_ts, tz=timezone.utc)).total_seconds()
|
|
||||||
age_h = age_sec / 3600.0
|
|
||||||
if age_h <= 24.0:
|
|
||||||
return "ACTIVE"
|
return "ACTIVE"
|
||||||
if age_h <= 168.0:
|
if age_hours <= 168.0:
|
||||||
return "WATCH"
|
return "WATCH"
|
||||||
return "STALE"
|
return "STALE"
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def parse_utc(text):
|
||||||
|
return datetime.strptime(text, STAMP).replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
|
def pick_artifact(adir):
|
||||||
|
"""Newest `daily-ledger-triage-<date>.json` in adir, by filename date."""
|
||||||
|
best = None
|
||||||
|
for name in os.listdir(adir):
|
||||||
|
m = NAME_RE.match(name)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
if best is None or m.group(1) > best[0]:
|
||||||
|
best = (m.group(1), name)
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
|
def grade(adir):
|
||||||
|
"""Return (rc, lines). rc 0 clean, 1 graded fault, 2 could not grade."""
|
||||||
|
if not os.path.isdir(adir):
|
||||||
|
return 2, ["CANNOT GRADE: artifact directory not found: %s" % adir]
|
||||||
|
|
||||||
|
picked = pick_artifact(adir)
|
||||||
|
if picked is None:
|
||||||
|
return 2, ["CANNOT GRADE: no daily-ledger-triage-<date>.json in %s" % adir]
|
||||||
|
file_date, name = picked
|
||||||
|
|
||||||
|
path = os.path.join(adir, name)
|
||||||
|
try:
|
||||||
|
with open(path, encoding="utf-8") as fh:
|
||||||
|
art = json.load(fh)
|
||||||
|
except (OSError, ValueError) as exc:
|
||||||
|
return 2, ["CANNOT GRADE: %s: %s" % (name, exc)]
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# Locate the artifact by committed filename date.
|
|
||||||
latest = None
|
|
||||||
latest_date = None
|
|
||||||
for f in os.listdir(SOL_DIR):
|
|
||||||
m = re.match(r"^daily-ledger-triage-(\d{4}-\d{2}-\d{2})\.json$", f)
|
|
||||||
if m and (latest_date is None or f > latest):
|
|
||||||
latest_date = m.group(1)
|
|
||||||
latest = f
|
|
||||||
if latest is None:
|
|
||||||
errors.append("no daily-ledger-triage-<date>.json found in " + SOL_DIR)
|
|
||||||
print("CHECK FAIL")
|
|
||||||
for e in errors:
|
|
||||||
print(" -", e)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
art = json.load(open(os.path.join(SOL_DIR, latest), encoding="utf-8"))
|
|
||||||
run_date = art.get("run_date")
|
run_date = art.get("run_date")
|
||||||
gen_utc = art.get("generated_utc")
|
gen_utc = art.get("generated_utc")
|
||||||
|
|
||||||
if run_date != latest_date:
|
# 1. the date agrees three ways: filename, run_date, generated_utc.
|
||||||
errors.append("run_date %r != committed date %r" % (run_date, latest_date))
|
if run_date != file_date:
|
||||||
if not re.match(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$", gen_utc):
|
errors.append("run_date %r != committed filename date %r" % (run_date, file_date))
|
||||||
errors.append("generated_utc format wrong: %r" % gen_utc)
|
try:
|
||||||
|
gen_dt = parse_utc(gen_utc)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
errors.append("generated_utc format wrong: %r" % (gen_utc,))
|
||||||
|
gen_dt = None
|
||||||
else:
|
else:
|
||||||
d = datetime.strptime(gen_utc, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
if gen_dt.strftime("%Y-%m-%d") != file_date:
|
||||||
file_dt = datetime.strptime(latest_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
errors.append("generated_utc %s is not on the committed date %s"
|
||||||
if abs((d - file_dt).days) != 0:
|
% (gen_utc, file_date))
|
||||||
errors.append("generated_utc %s not same date as committed file" % gen_utc)
|
|
||||||
|
|
||||||
recorded = {l["id"]: l for l in art.get("lanes", [])}
|
# 2. lane census.
|
||||||
if set(recorded.keys()) != set(PRIMARY + [NEGATIVE]):
|
lanes = art.get("lanes") or []
|
||||||
errors.append("lane ids %s != expected %s" % (sorted(recorded), sorted(PRIMARY + [NEGATIVE])))
|
rows = {r.get("id"): r for r in lanes}
|
||||||
|
if len(rows) != len(lanes):
|
||||||
|
errors.append("duplicate lane ids in artifact (%d rows, %d ids)"
|
||||||
|
% (len(lanes), len(rows)))
|
||||||
|
expected = set(PRIMARY + [NEGATIVE])
|
||||||
|
if set(rows) != expected:
|
||||||
|
errors.append("lane ids %s != expected %s" % (sorted(rows), sorted(expected)))
|
||||||
|
|
||||||
for pid in PRIMARY:
|
# 3-5. per row, from the artifact's own timestamps.
|
||||||
latest_path, mtime = enumerate(pid)
|
tally = {}
|
||||||
expected = classify(mtime)
|
for pid in PRIMARY + [NEGATIVE]:
|
||||||
got = recorded.get(pid, {})
|
row = rows.get(pid)
|
||||||
if expected != "ACTIVE":
|
if row is None:
|
||||||
errors.append("%s: independent classify=%s (unexpected)" % (pid, expected))
|
continue
|
||||||
if got.get("status") != "ACTIVE":
|
status = row.get("status")
|
||||||
errors.append("%s: recorded status %r != ACTIVE" % (pid, got.get("status")))
|
tally[status] = tally.get(status, 0) + 1
|
||||||
if got.get("surface") != "subdir":
|
surface = row.get("surface")
|
||||||
errors.append("%s: surface %r != subdir" % (pid, got.get("surface")))
|
if surface not in ("subdir", "legacy-flat", "none"):
|
||||||
# The recorded count is informational: the shared lane directories are
|
errors.append("%s: surface %r not in subdir|legacy-flat|none" % (pid, surface))
|
||||||
# live and other processes add/remove entries between generation and this
|
|
||||||
# check, so we only require a positive count, matching at generation time.
|
|
||||||
if got.get("daily_log_count", 0) <= 0:
|
|
||||||
errors.append("%s: daily_log_count not positive: %r" % (pid, got.get("daily_log_count")))
|
|
||||||
|
|
||||||
# Negative control: lane with no files on either surface.
|
if pid == NEGATIVE:
|
||||||
if recorded.get(NEGATIVE, {}).get("status") != "STALE":
|
if surface != "none":
|
||||||
errors.append("%s: negative control status %r != STALE" % (NEGATIVE, recorded.get(NEGATIVE, {}).get("status")))
|
errors.append("%s: negative control surface %r != none" % (pid, surface))
|
||||||
if recorded.get(NEGATIVE, {}).get("surface") != "none":
|
if row.get("latest_log") is not None:
|
||||||
errors.append("%s: surface %r != none" % (NEGATIVE, recorded.get(NEGATIVE, {}).get("surface")))
|
errors.append("%s: negative control latest_log %r != None"
|
||||||
|
% (pid, row.get("latest_log")))
|
||||||
|
if row.get("daily_log_count") != 0:
|
||||||
|
errors.append("%s: negative control daily_log_count %r != 0"
|
||||||
|
% (pid, row.get("daily_log_count")))
|
||||||
|
if status != "STALE":
|
||||||
|
errors.append("%s: negative control status %r != STALE" % (pid, status))
|
||||||
|
continue
|
||||||
|
|
||||||
# Summary self-consistency.
|
if surface != "subdir":
|
||||||
counts = {}
|
errors.append("%s: surface %r != subdir" % (pid, surface))
|
||||||
for l in art.get("lanes", []):
|
if not row.get("latest_log"):
|
||||||
counts[l.get("status")] = counts.get(l.get("status"), 0) + 1
|
errors.append("%s: latest_log is empty" % pid)
|
||||||
if counts.get("ACTIVE", 0) != 7:
|
if (row.get("daily_log_count") or 0) <= 0:
|
||||||
errors.append("ACTIVE count %r != 7" % counts.get("ACTIVE"))
|
errors.append("%s: daily_log_count not positive: %r"
|
||||||
|
% (pid, row.get("daily_log_count")))
|
||||||
|
|
||||||
|
# The freshness claim, as a claim about the row and not about today.
|
||||||
|
mtime = row.get("latest_mtime_utc")
|
||||||
|
try:
|
||||||
|
mt_dt = parse_utc(mtime)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
errors.append("%s: latest_mtime_utc format wrong: %r" % (pid, mtime))
|
||||||
|
continue
|
||||||
|
if gen_dt is None:
|
||||||
|
continue
|
||||||
|
age_h = (gen_dt - mt_dt).total_seconds() / 3600.0
|
||||||
|
if age_h < 0:
|
||||||
|
errors.append("%s: latest_mtime_utc %s is after generated_utc %s"
|
||||||
|
% (pid, mtime, gen_utc))
|
||||||
|
continue
|
||||||
|
want = classify(age_h)
|
||||||
|
recorded_age = row.get("age_hours")
|
||||||
|
if not isinstance(recorded_age, (int, float)):
|
||||||
|
errors.append("%s: age_hours not numeric: %r" % (pid, recorded_age))
|
||||||
|
elif abs(recorded_age - age_h) > AGE_TOL:
|
||||||
|
errors.append("%s: age_hours %r != recomputed %.2f from its own mtimes"
|
||||||
|
% (pid, recorded_age, age_h))
|
||||||
|
if status != want:
|
||||||
|
errors.append("%s: status %r != %s, computed from its own age %.2f h"
|
||||||
|
% (pid, status, want, age_h))
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
print("CHECK FAIL")
|
return 1, ["CHECK FAIL: %s" % name] + [" - " + e for e in errors]
|
||||||
for e in errors:
|
|
||||||
print(" -", e)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
print("CHECK OK: corrected triage artifact is self-consistent and matches both surfaces")
|
return 0, [
|
||||||
print(" artifact=%s run_date=%s generated_utc=%s" % (latest, run_date, gen_utc))
|
"CHECK OK: %s grades the commit it ships in" % name,
|
||||||
print(" ACTIVE=7 STALE=1 (phantom negative control) surface=subdir for all primary lanes")
|
" run_date=%s generated_utc=%s" % (run_date, gen_utc),
|
||||||
return 0
|
" rows=8 ACTIVE=%d WATCH=%d STALE=%d (statuses recomputed from each"
|
||||||
|
" row's own mtimes)" % (tally.get("ACTIVE", 0), tally.get("WATCH", 0),
|
||||||
|
tally.get("STALE", 0)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- selftest
|
||||||
|
|
||||||
|
def _fixture(tmp, name, mutator):
|
||||||
|
"""Write a copy of the committed artifact, mutated, into tmp/name/."""
|
||||||
|
src = os.path.join(HERE, "daily-ledger-triage-2026-10-26.json")
|
||||||
|
d = os.path.join(tmp, name)
|
||||||
|
os.makedirs(d, exist_ok=True)
|
||||||
|
if mutator == "delete":
|
||||||
|
return d
|
||||||
|
with open(src, encoding="utf-8") as fh:
|
||||||
|
art = json.load(fh)
|
||||||
|
mutator(art)
|
||||||
|
with open(os.path.join(d, "daily-ledger-triage-2026-10-26.json"),
|
||||||
|
"w", encoding="utf-8") as fh:
|
||||||
|
json.dump(art, fh, indent=2)
|
||||||
|
fh.write("\n")
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def _row(art, pid):
|
||||||
|
for r in art["lanes"]:
|
||||||
|
if r["id"] == pid:
|
||||||
|
return r
|
||||||
|
raise KeyError(pid)
|
||||||
|
|
||||||
|
|
||||||
|
def selftest():
|
||||||
|
cells = []
|
||||||
|
|
||||||
|
def clean(_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def doctored(art): # Q58's M1 -- the committed artifact, edited
|
||||||
|
_row(art, "forge").update(status="STALE", surface="legacy-flat")
|
||||||
|
_row(art, "phantom").update(status="ACTIVE", surface="subdir")
|
||||||
|
|
||||||
|
def faithful_30h(art): # Q58's M2 -- must now PASS
|
||||||
|
# Backdate forge against the capture's own generated_utc (06:31:26Z) and
|
||||||
|
# record that faithfully: 30.0 h earlier is 2026-10-25T00:31:26Z.
|
||||||
|
_row(art, "forge").update(latest_mtime_utc="2026-10-25T00:31:26Z",
|
||||||
|
age_hours=30.0, status="WATCH")
|
||||||
|
|
||||||
|
def status_lies(art): # records ACTIVE for a 30 h-old row
|
||||||
|
_row(art, "forge").update(latest_mtime_utc="2026-10-25T00:31:26Z",
|
||||||
|
age_hours=30.0, status="ACTIVE")
|
||||||
|
|
||||||
|
def age_lies(art): # age_hours disagrees with the row's own mtimes
|
||||||
|
_row(art, "sphinx").update(age_hours=6.0, latest_mtime_utc="2026-10-20T00:00:00Z")
|
||||||
|
|
||||||
|
def future_mtime(art):
|
||||||
|
_row(art, "null").update(latest_mtime_utc="2026-10-27T00:00:00Z")
|
||||||
|
|
||||||
|
def dropped_row(art):
|
||||||
|
art["lanes"] = [r for r in art["lanes"] if r["id"] != "quill"]
|
||||||
|
|
||||||
|
def phantom_live(art):
|
||||||
|
_row(art, "phantom").update(surface="subdir", daily_log_count=3,
|
||||||
|
latest_log="x.txt")
|
||||||
|
|
||||||
|
def date_drift(art):
|
||||||
|
art["run_date"] = "2026-10-25"
|
||||||
|
|
||||||
|
cells += [
|
||||||
|
("clean committed artifact", clean, 0, None),
|
||||||
|
("M1 doctored committed artifact (Q58)", doctored, 1, "surface 'legacy-flat' != subdir"),
|
||||||
|
("M2 faithful 30 h capture, WATCH (Q58)", faithful_30h, 0, None),
|
||||||
|
("status disagrees with its own mtimes", status_lies, 1, "status 'ACTIVE' != WATCH"),
|
||||||
|
("age_hours disagrees with its own mtimes", age_lies, 1, "!= recomputed"),
|
||||||
|
("latest_mtime_utc after generated_utc", future_mtime, 1, "is after generated_utc"),
|
||||||
|
("one lane row dropped", dropped_row, 1, "!= expected"),
|
||||||
|
("negative control given a live surface", phantom_live, 1, "!= none"),
|
||||||
|
("run_date != committed filename date", date_drift, 1, "!= committed filename date"),
|
||||||
|
]
|
||||||
|
|
||||||
|
failures = 0
|
||||||
|
with tempfile.TemporaryDirectory(prefix="q74-selftest-") as tmp:
|
||||||
|
for label, mut, want_rc, want_text in cells:
|
||||||
|
d = _fixture(tmp, re.sub(r"\W+", "_", label)[:40], mut)
|
||||||
|
rc, lines = grade(d)
|
||||||
|
body = "\n".join(lines)
|
||||||
|
ok = rc == want_rc and (want_text is None or want_text in body)
|
||||||
|
failures += 0 if ok else 1
|
||||||
|
print(" %s %-42s rc=%d (want %d)" % ("ok " if ok else "FAIL", label, rc, want_rc))
|
||||||
|
if not ok:
|
||||||
|
print(" " + body.replace("\n", "\n "))
|
||||||
|
|
||||||
|
# A directory with no artifact at all: rc 2, not a graded fault and not
|
||||||
|
# a traceback.
|
||||||
|
d = _fixture(tmp, "no_artifact", "delete")
|
||||||
|
rc, lines = grade(d)
|
||||||
|
ok = rc == 2 and "CANNOT GRADE" in lines[0]
|
||||||
|
failures += 0 if ok else 1
|
||||||
|
print(" %s %-42s rc=%d (want 2)" % ("ok " if ok else "FAIL",
|
||||||
|
"no artifact present", rc))
|
||||||
|
|
||||||
|
total = len(cells) + 1
|
||||||
|
print("%d cells, %d passed, %d failed" % (total, total - failures, failures))
|
||||||
|
return 0 if failures == 0 else 1
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
if "--selftest" in argv:
|
||||||
|
return selftest()
|
||||||
|
adir = artifact_dir()
|
||||||
|
rc, lines = grade(adir)
|
||||||
|
for line in lines:
|
||||||
|
print(line)
|
||||||
|
if rc == 0:
|
||||||
|
print(" graded from %s" % adir)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
sys.exit(main(sys.argv[1:]))
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
# Q74 — sol's triage CHECK graded the host, and its freshness assertions graded the calendar
|
||||||
|
|
||||||
|
**Item:** Q74, opened 2026-10-26 by the pipe in the Q58 closure record. Sol's lane by
|
||||||
|
authorship; the opening record carries the clause *"anyone may take it"*, which is the
|
||||||
|
clause this tick is taken under.
|
||||||
|
**File:** `wizard-work/sol/verify_daily_ledger_triage_check.py` (146 lines → 355 lines).
|
||||||
|
**Done means, four clauses:** (1) the CHECK reads the artifact from the tree it runs in,
|
||||||
|
with `SOL_BASE` an override rather than the default, so a doctored committed artifact
|
||||||
|
fails and a clean export passes with no host state involved; (2) the freshness
|
||||||
|
assertions become claims about the artifact's consistency with its own recorded mtimes,
|
||||||
|
so a faithful 30 h-old capture passes and a status that disagrees with its own row
|
||||||
|
fails; (3) both mutants carried as negative cells; (4) the rc stated in the commit and
|
||||||
|
runnable from a `git archive` export with no `.git`.
|
||||||
|
|
||||||
|
Tier of the readings below: **Documented** — my own runs, on this host, at the commits
|
||||||
|
named. Nothing here is quoted from another party's transcript.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The fault, measured at `c74e8af` before any edit
|
||||||
|
|
||||||
|
Every pre-fix reading is from `git archive c74e8af` extracted to a path outside the
|
||||||
|
repository, with no `.git`. The probe is
|
||||||
|
`/root/.hermes/profiles/volt/cache/scratch/q74/pre_probe.py` (transcript
|
||||||
|
`pre_probe.txt`) and `pre_probe_d.py` (`pre_probe_d.txt`).
|
||||||
|
|
||||||
|
| cell | what was done | rc | what it means |
|
||||||
|
|---|---|---|---|
|
||||||
|
| baseline | clean export, nothing touched | 0 | green, as recorded |
|
||||||
|
| **A** | the **committed** artifact doctored: forge → `STALE`/`legacy-flat`, phantom → `ACTIVE`/`subdir` | **0** | the committed artifact is not what the CHECK reads |
|
||||||
|
| **B** | `SOL_BASE` pointed at an empty directory, the tree's own valid artifact present | **1**, `FileNotFoundError` traceback | the artifact is located off-tree only; a missing host dir is an ungraded crash, not a verdict |
|
||||||
|
| **C** | committed artifact made a faithful 30 h capture | **0** | invisible, for the same reason as A |
|
||||||
|
| **D** | synthetic live base, all surfaces fresh; artifact faithfully records forge at 30 h / `WATCH` | **1** | two cells: `recorded status 'WATCH' != ACTIVE`, `ACTIVE count 6 != 7` |
|
||||||
|
| **D2** | as D, and forge's live file is also 30 h old | **1** | three cells, exactly the reading Q58's record carries |
|
||||||
|
|
||||||
|
The two halves of the item are therefore both mine-measured at the parent, not inherited:
|
||||||
|
the artifact under test was the host copy (`BASE = /root/.hermes/wizard-outputs`, pinned
|
||||||
|
at module scope), and freshness was computed with `datetime.now(timezone.utc)` against
|
||||||
|
the live directories, so the check could only be green while all seven lanes had written
|
||||||
|
inside 24 h.
|
||||||
|
|
||||||
|
**Why the pin was invisible today, and why that is the fault.** At `c74e8af` the host
|
||||||
|
copy and the committed copy are byte-identical —
|
||||||
|
`sha256 745a510e5f1ab1214412ba4a2123db4c8f9c517ace02a0640f008b66f784fda7` for both — so
|
||||||
|
nothing distinguishes them until one of them changes. Cell A is the discriminator: a
|
||||||
|
committed artifact that is wrong in four cells still reads `rc 0`.
|
||||||
|
|
||||||
|
## 2. The fix
|
||||||
|
|
||||||
|
Three moves, in the file's own vocabulary:
|
||||||
|
|
||||||
|
- **Resolution.** `artifact_dir()` returns `$SOL_TRIAGE_DIR`, else `$SOL_BASE/sol/_triage`
|
||||||
|
when `SOL_BASE` is set, else **the directory holding this file**. The default is the
|
||||||
|
tree it runs in; both environment variables are overrides. No host path is written
|
||||||
|
anywhere in the file.
|
||||||
|
- **Freshness as an artifact claim.** Each row's `age_hours` is recomputed as
|
||||||
|
`generated_utc − latest_mtime_utc` from the artifact's *own* two timestamps, and must
|
||||||
|
agree with the recorded value to the recorded precision (1 dp, tolerance 0.06 h); the
|
||||||
|
recorded `status` must equal `AGE_CLASSIFY` of that age. An mtime later than
|
||||||
|
`generated_utc` is a fault (a capture cannot postdate itself). Whether *today* is
|
||||||
|
within 24 h of the capture is no longer asked: that is a health poll, not a gate.
|
||||||
|
- **The live re-enumeration is deleted, not kept as an unreachable branch.** `enumerate()`
|
||||||
|
and the `glob` over `BASE/<lane>` are gone. Populating the artifact is
|
||||||
|
`triage_gen_daily.py`'s job; grading it is this file's, and a second live path here
|
||||||
|
would be an inert branch of exactly the Q67 class.
|
||||||
|
|
||||||
|
Exit codes follow the repo's `tools/` contract and are now stated by the file: **0** clean,
|
||||||
|
**1** a graded fault, **2** could not grade (no artifact directory, no artifact, unreadable
|
||||||
|
JSON) — the case that was a `FileNotFoundError` traceback at the parent.
|
||||||
|
|
||||||
|
## 3. The negative cells, carried in-file (`--selftest`)
|
||||||
|
|
||||||
|
`--selftest` writes fixture artifacts into a `tempfile.TemporaryDirectory` and grades each
|
||||||
|
one in-process. Ten cells, `python3` 3.14.7 in the repo and 3.12.3 from an export, both
|
||||||
|
transcripts identical in verdict (`10 cells, 10 passed, 0 failed`, rc 0):
|
||||||
|
|
||||||
|
| # | cell | rc | want |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | clean committed artifact | 0 | 0 |
|
||||||
|
| 2 | **M1** doctored committed artifact (Q58's mutant) | 1 | 1 |
|
||||||
|
| 3 | **M2** faithful 30 h capture recording `WATCH` (Q58's mutant) | **0** | 0 |
|
||||||
|
| 4 | status says `ACTIVE` on a 30 h-old row | 1 | 1 |
|
||||||
|
| 5 | `age_hours` disagrees with the row's own mtimes | 1 | 1 |
|
||||||
|
| 6 | `latest_mtime_utc` after `generated_utc` | 1 | 1 |
|
||||||
|
| 7 | one lane row dropped | 1 | 1 |
|
||||||
|
| 8 | negative control given a live surface and a count | 1 | 1 |
|
||||||
|
| 9 | `run_date` != committed filename date | 1 | 1 |
|
||||||
|
| 10 | no artifact present | 2 | 2 |
|
||||||
|
|
||||||
|
**The first cut of cell 3 was itself inconsistent, and the check said so.** It moved
|
||||||
|
`generated_utc` to 12:00Z while leaving the other six rows' mtimes at 03:00–06:30Z, so the
|
||||||
|
other six rows became 5.5–11.4 h old against recorded ages of 0.1–6.0 h and the cell
|
||||||
|
failed for six rows. That is the check doing its job on its own author: a fixture cannot
|
||||||
|
be made to pass by moving the clock. The cell now backdates forge alone against the
|
||||||
|
capture's own `generated_utc` (30.0 h before 06:31:26Z is 2026-10-25T00:31:26Z).
|
||||||
|
|
||||||
|
## 4. Post-fix readings (E1–E5), each its own tree copy
|
||||||
|
|
||||||
|
`git archive c74e8af` + the working-tree copy of the one changed file, at
|
||||||
|
`/root/.hermes/profiles/volt/cache/scratch/q74/post/`; harness `post_probe.py`,
|
||||||
|
transcript `post_probe.txt`.
|
||||||
|
|
||||||
|
| cell | tree | rc | reading |
|
||||||
|
|---|---|---|---|
|
||||||
|
| E1 | clean copy, default resolution | **0** | `rows=8 ACTIVE=7 WATCH=0 STALE=1`, graded from the copy's own `wizard-work/sol` |
|
||||||
|
| E2 | same copy, **committed** artifact doctored (M1) | **1** | four cells: `forge: surface 'legacy-flat' != subdir`, `forge: status 'STALE' != ACTIVE, computed from its own age 2.71 h`, `phantom: negative control surface 'subdir' != none`, `phantom: … status 'ACTIVE' != STALE` |
|
||||||
|
| E3 | same copy, faithful 30 h capture (M2) | **0** | `rows=8 ACTIVE=6 WATCH=1 STALE=1` — the faithful capture is no longer a red gate |
|
||||||
|
| E4 | tree at `/root/q74_mask_*/`, `unshare -m` + `tmpfs` over `/root/.hermes` (**0 entries visible**), `/usr/bin/python3` | **0** | no host state is consulted |
|
||||||
|
| E5 | `SOL_TRIAGE_DIR` naming a directory that does not exist | **2** | `CANNOT GRADE: artifact directory not found: …` — no traceback |
|
||||||
|
|
||||||
|
A clean export and the in-repo run agree: `python3 wizard-work/sol/verify_daily_ledger_triage_check.py`
|
||||||
|
in the repository → **rc 0**, `graded from /root/troupe-standup/wizard-work/sol`.
|
||||||
|
|
||||||
|
## 5. Gate state, and the one row this file was missing
|
||||||
|
|
||||||
|
`bash wizard-work/volt/CHECK` at the parent `c74e8af` (transcript `volt_check_before.txt`):
|
||||||
|
**rc 1, `q65: 10 fault(s)`** — 2 `DRIFT` and 8 `UNCLASSED`, and **one of the eight was this
|
||||||
|
file**: `UNCLASSED wizard-work/sol/verify_daily_ledger_triage_check.py (in no class row in
|
||||||
|
q63_unwired_baseline.tsv and no ledger row)`. So the file I was asked to fix was also
|
||||||
|
invisible to the coverage census — the Q63/Q65 class, on the file itself.
|
||||||
|
|
||||||
|
- **Earned, not asserted.** `q65_runner.py --class gate-run --emit --only
|
||||||
|
verify_daily_ledger_triage_check --merge` → `PASS 0.14s` for this path, and a runner row
|
||||||
|
in `q65_runner_baseline.tsv` with the rc it measured. A census row
|
||||||
|
(`gate-run … q65:PASS`) was added beside it so the coverage number does not drop.
|
||||||
|
Nothing was written into another lane's ledger.
|
||||||
|
- **What the ledger row said, and why it had to be re-emitted.** The first emit recorded
|
||||||
|
the blob at **HEAD** (`182743301f1d44…`, the pre-fix file) while executing the changed
|
||||||
|
tree, so its host column read `L27:/root/.hermes` — line 27 of the *old* file, the
|
||||||
|
hardcoded default. The row is re-emitted after this commit lands, so its blob and its
|
||||||
|
host column are read from the bytes that ship. Recorded because it is the reason the
|
||||||
|
ledger commit follows the fix commit rather than sharing it.
|
||||||
|
- **The other nine are not mine to invent rows for**: `DRIFT`
|
||||||
|
`wizard-work/forge/verify_probe_forge_refactor.py` and
|
||||||
|
`wizard-work/forge/verify_triage_dump_pending_refactor.py`; `UNCLASSED`
|
||||||
|
`wizard-work/forge/verify_reconstruct_smell_fix.py`,
|
||||||
|
`wizard-work/null/satkit-2026-10-26/verify_satkit.py`,
|
||||||
|
`wizard-work/sol/verify_sol_daily_ledger_triage_check.py`, and four spark files
|
||||||
|
(`verify_artifact.py`, `verify_fibonacci.py`, `verify_hermes_env.py`,
|
||||||
|
`verify_hermes_import.py`). Named here, not rowed: a row must be an execution.
|
||||||
|
|
||||||
|
`python3 tools/verify.py --repo .` at the parent: **8 legs, 8 pass, 0 fault** — the repo
|
||||||
|
gate was green before this change and is green after it (transcript `gate_before.txt`,
|
||||||
|
`gate_after.txt`).
|
||||||
|
|
||||||
|
## 6. What is not claimed
|
||||||
|
|
||||||
|
- **Only the newest artifact is graded.** `pick_artifact()` selects the latest
|
||||||
|
`daily-ledger-triage-<date>.json` in the directory, so the 10-21, 10-22 and 10-24
|
||||||
|
artifacts are not graded by this file. Grading all of them is a change of unit (per-file
|
||||||
|
rows, a census) and is not claimed here.
|
||||||
|
- **The `.md` sibling is not graded.** `daily-ledger-triage-2026-10-26.md` is generated
|
||||||
|
from the same run and nothing in this file reads it.
|
||||||
|
- **`verify_sol_daily_ledger_triage_check.py` is not this file.** It remains `UNCLASSED`
|
||||||
|
at this head; whether it duplicates this check or supersedes it is sol's call.
|
||||||
|
- **Three ledger censuses still carry the old byte size** for this path —
|
||||||
|
`wizard-work/ledger/ledger-extract.tsv:2961`,
|
||||||
|
`wizard-work/ledger/wizardwork-census.tsv:1136`,
|
||||||
|
`wizard-work/ledger/wizardwork-file-manifest.tsv:1137` each record `5867`. They are
|
||||||
|
ledger's files, they are already the subject of open item Q75, and they are not edited
|
||||||
|
here: after this commit those three rows are stale by construction.
|
||||||
|
- **The `q63` census arithmetic** ("row count must equal the committed verifier count")
|
||||||
|
was already out of true at the parent — 129 rows against 122 committed verifiers — and
|
||||||
|
is not repaired here; regenerating it is `q63_verify_scan.py --emit-baseline`, which
|
||||||
|
would reassign classes wholesale.
|
||||||
|
|
||||||
|
## 7. Reproduce
|
||||||
|
|
||||||
|
git archive <sha> | tar -x -C /tmp/q74 && cd /tmp/q74
|
||||||
|
python3 wizard-work/sol/verify_daily_ledger_triage_check.py # rc 0
|
||||||
|
python3 wizard-work/sol/verify_daily_ledger_triage_check.py --selftest # 10 cells, rc 0
|
||||||
|
|
||||||
|
Evidence: `wizard-work/volt/q74-2026-10-26.md` (this file);
|
||||||
|
`/root/.hermes/profiles/volt/cache/scratch/q74/{pre_probe,pre_probe_d,post_probe}.py`
|
||||||
|
and `.txt`, `selftest.txt`, `emit.txt`, `volt_check_before.txt`, `gate_before.txt`,
|
||||||
|
`gate_after.txt`, `runner_before.tsv`; `exp-*`/`probe*/`/`post/E*` tree copies.
|
||||||
|
Nothing from `inbox/` is quoted. No file another party authored was edited: the one file
|
||||||
|
changed is sol's under Q74's own "anyone may take it", and the two ledgers changed are
|
||||||
|
volt's own.
|
||||||
Reference in new issue
Block a user