null: the entry and its control locate their own checkout; a replay writes nothing and CHECK runs both (Q57 residual)
This commit is contained in:
1 parent
5cae75b672
commit
912e8a85d8
4 files changed
+206
-37
No files matched your search
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# null lane gate -- one command, and the exit code IS the verdict.
|
||||||
|
#
|
||||||
|
# 0 the entry is bound to the bytes it grades, and the negative control holds
|
||||||
|
# 1 a graded fault (check.py or the control reported one)
|
||||||
|
# 2 could not grade (no python3, not a checkout, fixture unavailable)
|
||||||
|
#
|
||||||
|
# It resolves its own directory, so the copy in a clone grades that clone --
|
||||||
|
# Q62's rule, applied to the gate that writes the CHECK line (2026-10-25).
|
||||||
|
#
|
||||||
|
# bash wizard-work/null/CHECK # from the repository root
|
||||||
|
set -u
|
||||||
|
|
||||||
|
d=$(dirname "$(readlink -f "$0")")
|
||||||
|
|
||||||
|
echo "== check.py: the entry against the remote main =="
|
||||||
|
python3 "$d/check.py"
|
||||||
|
a=$?
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== q57_mutation.py: the gate's own negative control =="
|
||||||
|
python3 "$d/q57_mutation.py"
|
||||||
|
b=$?
|
||||||
|
|
||||||
|
if [ "$a" -eq 0 ] && [ "$b" -eq 0 ]; then
|
||||||
|
echo
|
||||||
|
echo "LANE GATE GREEN"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "$a" -eq 2 ] || [ "$b" -eq 2 ]; then
|
||||||
|
echo
|
||||||
|
echo "LANE GATE: COULD NOT GRADE (check.py rc=$a, q57_mutation.py rc=$b)"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
echo "LANE GATE: FAULT (check.py rc=$a, q57_mutation.py rc=$b)"
|
||||||
|
exit 1
|
||||||
@@ -6,7 +6,8 @@ Exit code IS the verdict: 0 = every claim below holds, 1 = broken.
|
|||||||
python3 /root/troupe-standup/wizard-work/null/check.py
|
python3 /root/troupe-standup/wizard-work/null/check.py
|
||||||
|
|
||||||
What this gate measures, in order:
|
What this gate measures, in order:
|
||||||
1. the four files exist, compile, expose the documented API;
|
1. the five files exist, compile, expose the documented API, and CHECK --
|
||||||
|
the lane gate the entry's CHECK line names -- really runs check.py;
|
||||||
2. craft() and render() are pure functions of their arguments -- no clock;
|
2. craft() and render() are pure functions of their arguments -- no clock;
|
||||||
3. the log's declared commit is real, authored by null, and is an ancestor of
|
3. the log's declared commit is real, authored by null, and is an ancestor of
|
||||||
the commit the REMOTE names for refs/heads/main (ls-remote, not the clone);
|
the commit the REMOTE names for refs/heads/main (ls-remote, not the clone);
|
||||||
@@ -46,11 +47,11 @@ import uuid
|
|||||||
MYDIR = os.path.dirname(os.path.abspath(__file__))
|
MYDIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
REPO = os.path.dirname(os.path.dirname(MYDIR))
|
REPO = os.path.dirname(os.path.dirname(MYDIR))
|
||||||
LOGNAME = "wizard.log.md"
|
LOGNAME = "wizard.log.md"
|
||||||
NEED = ("wizard.log.md", "wizard.py", "check.py", "q57_mutation.py")
|
NEED = ("wizard.log.md", "wizard.py", "check.py", "q57_mutation.py", "CHECK")
|
||||||
LOGPATH = "wizard-work/null/" + LOGNAME
|
LOGPATH = "wizard-work/null/" + LOGNAME
|
||||||
GATEPATH = "wizard-work/null/check.py"
|
GATEPATH = "wizard-work/null/check.py"
|
||||||
WORK_FILES = ("wizard-work/null/wizard.py", "wizard-work/null/check.py",
|
WORK_FILES = ("wizard-work/null/wizard.py", "wizard-work/null/check.py",
|
||||||
"wizard-work/null/q57_mutation.py")
|
"wizard-work/null/q57_mutation.py", "wizard-work/null/CHECK")
|
||||||
PASSED, FAILED = [], []
|
PASSED, FAILED = [], []
|
||||||
|
|
||||||
|
|
||||||
@@ -163,6 +164,15 @@ def main():
|
|||||||
check("q57_mutation.py (the committed negative control) compiles",
|
check("q57_mutation.py (the committed negative control) compiles",
|
||||||
_compiles(open(control, encoding="utf-8").read() if os.path.isfile(control) else "",
|
_compiles(open(control, encoding="utf-8").read() if os.path.isfile(control) else "",
|
||||||
"q57_mutation.py"))
|
"q57_mutation.py"))
|
||||||
|
# The entry's CHECK line names CHECK, so CHECK must exist and must still be
|
||||||
|
# a runner for this file and for the control: a CHECK that stopped calling
|
||||||
|
# either would leave the published verdict pointing at a gate that no
|
||||||
|
# longer runs what the entry claims (2026-10-25).
|
||||||
|
chk = os.path.join(MYDIR, "CHECK")
|
||||||
|
chk_txt = open(chk, encoding="utf-8").read() if os.path.isfile(chk) else ""
|
||||||
|
check("CHECK runs check.py and the negative control (the CHECK line is not a dead pointer)",
|
||||||
|
"check.py" in chk_txt and "q57_mutation.py" in chk_txt,
|
||||||
|
("%d bytes" % len(chk_txt)) if chk_txt else "missing")
|
||||||
ns = {"__name__": "wizard_module", "__file__": os.path.join(MYDIR, "wizard.py")}
|
ns = {"__name__": "wizard_module", "__file__": os.path.join(MYDIR, "wizard.py")}
|
||||||
if ok_compile:
|
if ok_compile:
|
||||||
exec(compile(src, "wizard.py", "exec"), ns)
|
exec(compile(src, "wizard.py", "exec"), ns)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Exit code IS the verdict: 0 = the gate behaves the way its own docstring says on
|
Exit code IS the verdict: 0 = the gate behaves the way its own docstring says on
|
||||||
a mutated entry, 1 = it does not. Nothing here is asserted; every cell is a run.
|
a mutated entry, 1 = it does not. Nothing here is asserted; every cell is a run.
|
||||||
|
|
||||||
python3 /root/troupe-standup/wizard-work/null/q57_mutation.py
|
python3 wizard-work/null/q57_mutation.py
|
||||||
|
|
||||||
WHY A FIXTURE, AND NOT THE LIVE TREE
|
WHY A FIXTURE, AND NOT THE LIVE TREE
|
||||||
`python3 check.py` reads whatever is on disk, so running it in the shared
|
`python3 check.py` reads whatever is on disk, so running it in the shared
|
||||||
@@ -15,7 +15,14 @@ WHY A FIXTURE, AND NOT THE LIVE TREE
|
|||||||
fixture is the frozen bare repo, so a concurrent push by another wizard
|
fixture is the frozen bare repo, so a concurrent push by another wizard
|
||||||
cannot change what the test is measuring.
|
cannot change what the test is measuring.
|
||||||
|
|
||||||
WHAT IT MEASURES -- seven cases, expectations computed here, never hardcoded
|
WHERE THE REPO IS
|
||||||
|
Resolved from this file's own path (`git rev-parse --show-toplevel`), so the
|
||||||
|
copy in a clone grades that clone. It used to default to the host path
|
||||||
|
/root/troupe-standup, which meant a clone's copy silently snapshotted the
|
||||||
|
host instead of the tree it sat in -- Q62's class, measured 2026-10-25.
|
||||||
|
NULL_REPO=<path> overrides it, and the path actually used is printed.
|
||||||
|
|
||||||
|
WHAT IT MEASURES -- eight cases, expectations computed here, never hardcoded
|
||||||
case 0 clean clone -> rc 0, ALL CHECKS GREEN
|
case 0 clean clone -> rc 0, ALL CHECKS GREEN
|
||||||
case 1 one byte inside a work-file bullet -> rc 1, names that line
|
case 1 one byte inside a work-file bullet -> rc 1, names that line
|
||||||
case 2 the operator line -> rc 1, names that line
|
case 2 the operator line -> rc 1, names that line
|
||||||
@@ -23,15 +30,22 @@ WHAT IT MEASURES -- seven cases, expectations computed here, never hardcoded
|
|||||||
case 4 one line appended -> rc 1, names the line past the end
|
case 4 one line appended -> rc 1, names the line past the end
|
||||||
case 5 final newline becomes CRLF -> rc 1, "a line terminator differs",
|
case 5 final newline becomes CRLF -> rc 1, "a line terminator differs",
|
||||||
and NO line number is invented
|
and NO line number is invented
|
||||||
case 6 the published gate moved on -> rc 1, names check.py, while the
|
case 6 replay in the clone -> the clone's entry is byte-identical,
|
||||||
|
its mtime is untouched, `git status`
|
||||||
|
is empty, and the HOST's entry is
|
||||||
|
untouched (self-location)
|
||||||
|
case 7 the published gate moved on -> rc 1, names check.py, while the
|
||||||
entry check still PASSES
|
entry check still PASSES
|
||||||
|
|
||||||
Case 5 was GREEN before this artifact. Both sides of the old comparison ran
|
Case 5 was GREEN before this artifact. Both sides of the old comparison ran
|
||||||
under universal-newline translation (subprocess text=True and open(...,
|
under universal-newline translation (subprocess text=True and open(...,
|
||||||
encoding=...)), so the CRLF never reached the comparison: a doctored entry
|
encoding=...)), so the CRLF never reached the comparison: a doctored entry
|
||||||
read "byte-identical". Transcript of that run: ../q57-binding/transcript-before.txt.
|
read "byte-identical". Case 7 was impossible before: nothing compared the
|
||||||
Case 6 was impossible before: nothing compared the gate on disk with the gate
|
gate on disk with the gate on main, so an entry could be certified by a
|
||||||
on main, so an entry could be certified by a private build.
|
private build. Case 6 was added 2026-10-25: before it, the builder carried
|
||||||
|
REPO and HERE as constants, so `python3 wizard-work/null/wizard.py` in a
|
||||||
|
clone rewrote the HOST's entry and left the clone alone -- a replay that
|
||||||
|
reproduces nothing is not a replay.
|
||||||
|
|
||||||
Expected line numbers are computed two independent ways and both must agree:
|
Expected line numbers are computed two independent ways and both must agree:
|
||||||
by locating the mutated text in the graded bytes, and by a first-difference
|
by locating the mutated text in the graded bytes, and by a first-difference
|
||||||
@@ -46,9 +60,23 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
REPO = os.environ.get("NULL_REPO", "/root/troupe-standup")
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def _toplevel(start):
|
||||||
|
"""The repository root containing `start`, or "" when there is none."""
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["git", "-C", start, "rev-parse", "--show-toplevel"],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
except OSError:
|
||||||
|
return ""
|
||||||
|
return r.stdout.strip() if r.returncode == 0 else ""
|
||||||
|
|
||||||
|
|
||||||
|
REPO = os.environ.get("NULL_REPO") or _toplevel(HERE)
|
||||||
LOGREL = "wizard-work/null/wizard.log.md"
|
LOGREL = "wizard-work/null/wizard.log.md"
|
||||||
GATEREL = "wizard-work/null/check.py"
|
GATEREL = "wizard-work/null/check.py"
|
||||||
|
BUILDER = "wizard-work/null/wizard.py"
|
||||||
PASSED, FAILED = [], []
|
PASSED, FAILED = [], []
|
||||||
|
|
||||||
|
|
||||||
@@ -87,6 +115,10 @@ def first_diff_scan(want, got):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
print("grading repo: %s" % (REPO or "<none: this file is not in a git checkout>"))
|
||||||
|
if not REPO:
|
||||||
|
print("CANNOT GRADE: no repository root from %s (set NULL_REPO)" % HERE)
|
||||||
|
return 2
|
||||||
root = tempfile.mkdtemp(prefix="q57-mutation-")
|
root = tempfile.mkdtemp(prefix="q57-mutation-")
|
||||||
bare = os.path.join(root, "origin.git")
|
bare = os.path.join(root, "origin.git")
|
||||||
fx = os.path.join(root, "fixture")
|
fx = os.path.join(root, "fixture")
|
||||||
@@ -96,12 +128,14 @@ def main():
|
|||||||
|
|
||||||
r = sh(["git", "clone", "--bare", "--quiet", REPO, bare])
|
r = sh(["git", "clone", "--bare", "--quiet", REPO, bare])
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
print("FAIL cannot snapshot the repo: " + r.stderr.decode("utf-8", "replace")[:200])
|
print("CANNOT GRADE cannot snapshot the repo: "
|
||||||
return 1
|
+ r.stderr.decode("utf-8", "replace")[:200])
|
||||||
|
return 2
|
||||||
r = sh(["git", "clone", "--quiet", bare, fx])
|
r = sh(["git", "clone", "--quiet", bare, fx])
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
print("FAIL cannot clone the fixture: " + r.stderr.decode("utf-8", "replace")[:200])
|
print("CANNOT GRADE cannot clone the fixture: "
|
||||||
return 1
|
+ r.stderr.decode("utf-8", "replace")[:200])
|
||||||
|
return 2
|
||||||
|
|
||||||
def run_gate():
|
def run_gate():
|
||||||
p = subprocess.run([sys.executable, gatep], capture_output=True, text=True)
|
p = subprocess.run([sys.executable, gatep], capture_output=True, text=True)
|
||||||
@@ -204,11 +238,46 @@ def main():
|
|||||||
check("case 5 the byte count is printed, so the 1-byte delta is visible",
|
check("case 5 the byte count is printed, so the 1-byte delta is visible",
|
||||||
"%d bytes" % len(graded) in out and "%d bytes" % len(mutated5) in out)
|
"%d bytes" % len(graded) in out and "%d bytes" % len(mutated5) in out)
|
||||||
|
|
||||||
# ---- case 6: the published gate has moved on --------------------------
|
# ---- case 6: a replay in the clone reproduces it and touches nothing ---
|
||||||
|
# Before 2026-10-25 the builder held REPO and HERE as constants, so this
|
||||||
|
# exact run rewrote the HOST's entry and left the clone alone: mtime moved
|
||||||
|
# at /root/troupe-standup, the clone's file never opened. Both halves are
|
||||||
|
# measured here -- the clone's bytes and mtime, and the host's.
|
||||||
|
reset_log()
|
||||||
|
host_log = os.path.join(REPO, LOGREL)
|
||||||
|
clone_before = open(logp, "rb").read()
|
||||||
|
mtime_before = os.stat(logp).st_mtime_ns
|
||||||
|
host_before = open(host_log, "rb").read()
|
||||||
|
host_mtime = os.stat(host_log).st_mtime_ns
|
||||||
|
p = subprocess.run([sys.executable, os.path.join(fx, BUILDER)], cwd=fx,
|
||||||
|
capture_output=True, text=True)
|
||||||
|
clone_after = open(logp, "rb").read()
|
||||||
|
mtime_after = os.stat(logp).st_mtime_ns
|
||||||
|
host_after = open(host_log, "rb").read()
|
||||||
|
status = git(fx, "status", "--porcelain").stdout.decode("utf-8", "replace").strip()
|
||||||
|
check("case 6 the fixture's builder exits 0", p.returncode == 0,
|
||||||
|
"rc %d" % p.returncode)
|
||||||
|
check("case 6 the replay reproduces the entry byte-for-byte",
|
||||||
|
clone_after == clone_before,
|
||||||
|
"%d bytes" % len(clone_after) if clone_after == clone_before
|
||||||
|
else "clone entry changed: %d -> %d bytes" % (len(clone_before), len(clone_after)))
|
||||||
|
check("case 6 the replay opens no file for writing (mtime unchanged)",
|
||||||
|
mtime_after == mtime_before,
|
||||||
|
"mtime %d" % mtime_after if mtime_after == mtime_before
|
||||||
|
else "mtime moved %d -> %d" % (mtime_before, mtime_after))
|
||||||
|
check("case 6 the worktree is clean after the replay", status == "",
|
||||||
|
"clean" if status == "" else status.splitlines()[0])
|
||||||
|
check("case 6 the HOST's entry is untouched: the builder grades its own checkout",
|
||||||
|
host_after == host_before and os.stat(host_log).st_mtime_ns == host_mtime,
|
||||||
|
"host %s untouched" % LOGREL
|
||||||
|
if host_after == host_before and os.stat(host_log).st_mtime_ns == host_mtime
|
||||||
|
else "the host's entry was written by a clone-side replay")
|
||||||
|
|
||||||
|
# ---- case 7: the published gate has moved on --------------------------
|
||||||
reset_log()
|
reset_log()
|
||||||
m = re.search(r"^- base commit: ([0-9a-f]{40})$", text, re.M)
|
m = re.search(r"^- base commit: ([0-9a-f]{40})$", text, re.M)
|
||||||
if not m:
|
if not m:
|
||||||
check("case 6 the entry names a 40-hex base commit", False, "no base commit line")
|
check("case 7 the entry names a 40-hex base commit", False, "no base commit line")
|
||||||
else:
|
else:
|
||||||
named = m.group(1)
|
named = m.group(1)
|
||||||
with open(gatep, "ab") as fh:
|
with open(gatep, "ab") as fh:
|
||||||
@@ -216,7 +285,7 @@ def main():
|
|||||||
git(fx, "-c", "user.name=null", "-c", "user.email=null@agents.invalid",
|
git(fx, "-c", "user.name=null", "-c", "user.email=null@agents.invalid",
|
||||||
"commit", "-aqm", "fixture: publish a gate that moved on")
|
"commit", "-aqm", "fixture: publish a gate that moved on")
|
||||||
push = git(fx, "push", "--quiet", "origin", "HEAD:main")
|
push = git(fx, "push", "--quiet", "origin", "HEAD:main")
|
||||||
check("case 6 fixture: the frozen origin advanced past the named commit",
|
check("case 7 fixture: the frozen origin advanced past the named commit",
|
||||||
push.returncode == 0,
|
push.returncode == 0,
|
||||||
"origin/main is now %s" % git(fx, "rev-parse", "HEAD").stdout.decode().strip()[:12])
|
"origin/main is now %s" % git(fx, "rev-parse", "HEAD").stdout.decode().strip()[:12])
|
||||||
# Put the GRADED gate back on disk: disk == named commit's blob (so the
|
# Put the GRADED gate back on disk: disk == named commit's blob (so the
|
||||||
@@ -224,10 +293,10 @@ def main():
|
|||||||
with open(gatep, "wb") as fh:
|
with open(gatep, "wb") as fh:
|
||||||
fh.write(git(fx, "show", "%s:%s" % (named, GATEREL)).stdout)
|
fh.write(git(fx, "show", "%s:%s" % (named, GATEREL)).stdout)
|
||||||
rc, out = run_gate()
|
rc, out = run_gate()
|
||||||
check("case 6 a gate that main does not carry is refused",
|
check("case 7 a gate that main does not carry is refused",
|
||||||
rc == 1 and "FAIL the gate on disk is the gate published on the remote main" in out,
|
rc == 1 and "FAIL the gate on disk is the gate published on the remote main" in out,
|
||||||
"rc %d" % rc)
|
"rc %d" % rc)
|
||||||
check("case 6 isolation: the entry itself is still byte-identical on main",
|
check("case 7 isolation: the entry itself is still byte-identical on main",
|
||||||
"FAIL entry bytes are the blob on the remote main" not in out
|
"FAIL entry bytes are the blob on the remote main" not in out
|
||||||
and "PASS entry bytes are the blob on the remote main" in out,
|
and "PASS entry bytes are the blob on the remote main" in out,
|
||||||
"only the published-gate cell moved")
|
"only the published-gate cell moved")
|
||||||
|
|||||||
+71
-18
@@ -3,12 +3,26 @@
|
|||||||
|
|
||||||
Rebuilds the log from parameters, never from the wall clock:
|
Rebuilds the log from parameters, never from the wall clock:
|
||||||
|
|
||||||
python3 wizard.py # writes wizard.log.md next to this file
|
python3 wizard-work/null/wizard.py # writes wizard.log.md next to this file
|
||||||
python3 check.py # exit 0 == artifact is real, exit 1 == broken
|
python3 wizard-work/null/check.py # exit 0 == artifact is real, 1 == broken
|
||||||
|
|
||||||
|
python3 wizard-work/null/wizard.py --restamp # date this rebuild now
|
||||||
|
python3 wizard-work/null/wizard.py --stamp "2026-10-25 06:00 UTC"
|
||||||
|
|
||||||
Determinism. craft() takes its stamp as a required parameter, so its output is
|
Determinism. craft() takes its stamp as a required parameter, so its output is
|
||||||
a function of its arguments; render() never reads the clock or the filesystem.
|
a function of its arguments; render() never reads the clock or the filesystem.
|
||||||
Replaying a log whose stamp is already current rewrites it byte-identically.
|
Replaying a log whose stamp is already current writes nothing at all: main()
|
||||||
|
compares the rendered bytes with the file and only opens it for writing when
|
||||||
|
they differ. Two replays in any checkout leave the worktree clean. The clock
|
||||||
|
is read in exactly one place -- main(), when the log does not exist yet or when
|
||||||
|
--restamp asks for a new date -- and never inside craft() or render().
|
||||||
|
|
||||||
|
Location. REPO and HERE are derived from this file's own path, never from a
|
||||||
|
constant: the same command run in a clone grades and writes that clone. The
|
||||||
|
paths the entry *prints* are repository-relative, so the entry renders
|
||||||
|
byte-identically in every checkout -- an absolute path in the log would make
|
||||||
|
render() depend on where the checkout lives and the round-trip would fail
|
||||||
|
everywhere but one host.
|
||||||
|
|
||||||
Binding -- and why it is NOT "the commit whose tree holds these bytes".
|
Binding -- and why it is NOT "the commit whose tree holds these bytes".
|
||||||
The entry names a commit SHA, so its own bytes can never be the blob of the
|
The entry names a commit SHA, so its own bytes can never be the blob of the
|
||||||
@@ -27,28 +41,47 @@ remote:
|
|||||||
Nothing here is self-referential: the work files do not contain the SHA, and
|
Nothing here is self-referential: the work files do not contain the SHA, and
|
||||||
the entry is committed after the work commit it names.
|
the entry is committed after the work commit it names.
|
||||||
|
|
||||||
Import-free by design -- check.py exec()s this file with no __file__ in scope --
|
Import-free by design -- stdlib only, no module-level dunder reads beyond
|
||||||
stdlib only, no module-level dunder reads.
|
locating this file. check.py exec()s this file with __file__ pointing at the
|
||||||
|
real path, so self-location works under exec() as well as under `python3`.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
LANE = "Codeforge - hard coding and agentic reasoning"
|
LANE = "Codeforge - hard coding and agentic reasoning"
|
||||||
REPO = "/root/troupe-standup"
|
try:
|
||||||
HERE = "/root/troupe-standup/wizard-work/null"
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
except NameError: # exec()'d without __file__: fall back to the documented cwd
|
||||||
|
HERE = os.path.abspath(os.path.join("wizard-work", "null"))
|
||||||
|
|
||||||
|
|
||||||
|
def _toplevel(start):
|
||||||
|
"""The repository root containing `start`, or "" when there is none."""
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["git", "-C", start, "rev-parse", "--show-toplevel"],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
except OSError:
|
||||||
|
return ""
|
||||||
|
return r.stdout.strip() if r.returncode == 0 else ""
|
||||||
|
|
||||||
|
|
||||||
|
REPO = _toplevel(HERE)
|
||||||
|
RELPATH = "wizard-work/null"
|
||||||
LOGNAME = "wizard.log.md"
|
LOGNAME = "wizard.log.md"
|
||||||
LOGPATH = "wizard-work/null/" + LOGNAME
|
LOGPATH = RELPATH + "/" + LOGNAME
|
||||||
STAMP_FMT = "%Y-%m-%d %H:%M UTC"
|
STAMP_FMT = "%Y-%m-%d %H:%M UTC"
|
||||||
STAMP_RE = re.compile(r"self-verified at [0-9-]+ [0-9:]+ UTC")
|
STAMP_RE = re.compile(r"self-verified at [0-9-]+ [0-9:]+ UTC")
|
||||||
TASK = "name the line a mutated standup entry changed, and commit the negative control"
|
TASK = "name the line a mutated standup entry changed, and commit the negative control"
|
||||||
AREA = "wizard-work/null"
|
AREA = "wizard-work/null"
|
||||||
OPERATOR = "null"
|
OPERATOR = "null"
|
||||||
WORK = [
|
WORK = [
|
||||||
"wizard.py: parameterized builder; the stamp and the commit are arguments, not the clock",
|
"wizard.py: parameterized builder; the stamp and the commit are arguments, not the clock, and it locates its own checkout",
|
||||||
"check.py: gate re-derives the carrying commit and the remote entry, never trusts the local clone; a mutation is named by its LINE, and both comparisons are byte-exact",
|
"check.py: gate re-derives the carrying commit and the remote entry, never trusts the local clone; a mutation is named by its LINE, and both comparisons are byte-exact",
|
||||||
"q57_mutation.py: the committed negative control -- seven mutation cases in a frozen clone, exit code is the verdict",
|
"q57_mutation.py: the committed negative control -- eight cases in a frozen clone, including a replay that must not touch the host, exit code is the verdict",
|
||||||
|
"CHECK: the lane gate -- one command that runs check.py and the control and grades both",
|
||||||
"wizard.log.md: self-describing entry naming the commit that carries the work files",
|
"wizard.log.md: self-describing entry naming the commit that carries the work files",
|
||||||
]
|
]
|
||||||
EVIDENCE = [
|
EVIDENCE = [
|
||||||
@@ -68,6 +101,10 @@ EVIDENCE = [
|
|||||||
"check.py: render() round-trip; both reads disable newline translation"),
|
"check.py: render() round-trip; both reads disable newline translation"),
|
||||||
("craft() deterministic in its stamp argument", "measured",
|
("craft() deterministic in its stamp argument", "measured",
|
||||||
"check.py: same args -> same bytes; different stamp -> different verdict"),
|
"check.py: same args -> same bytes; different stamp -> different verdict"),
|
||||||
|
("a replay of the entry writes nothing, in any checkout", "measured",
|
||||||
|
"q57_mutation.py case 6: the fixture's wizard.py run twice, bytes and mtime unchanged, `git status` empty, the host's log untouched"),
|
||||||
|
("the builder locates its own checkout, not this host", "measured",
|
||||||
|
"wizard.py: REPO/HERE from __file__ + `git rev-parse --show-toplevel`; q57_mutation.py case 6 runs it inside a clone"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -91,7 +128,7 @@ def parse_log(text):
|
|||||||
def craft(task, area, lane, commit, operator, stamp, log=LOGNAME):
|
def craft(task, area, lane, commit, operator, stamp, log=LOGNAME):
|
||||||
"""Build one standup entry. stamp is a parameter: replay is reproducible."""
|
"""Build one standup entry. stamp is a parameter: replay is reproducible."""
|
||||||
verdict = "VERDICT: %s - %s built and self-verified at %s." % (task, area, stamp)
|
verdict = "VERDICT: %s - %s built and self-verified at %s." % (task, area, stamp)
|
||||||
check = "CHECK: python3 %s/check.py" % HERE
|
check = "CHECK: bash %s/CHECK" % RELPATH
|
||||||
summary = "%s lane entry: %s" % (lane, "; ".join(WORK))
|
summary = "%s lane entry: %s" % (lane, "; ".join(WORK))
|
||||||
return verdict, check, summary
|
return verdict, check, summary
|
||||||
|
|
||||||
@@ -128,10 +165,10 @@ def render(task, area, lane, commit, operator, stamp, work=WORK, log=LOGNAME):
|
|||||||
|
|
||||||
```
|
```
|
||||||
python3 %s/wizard.py
|
python3 %s/wizard.py
|
||||||
python3 %s/check.py
|
bash %s/CHECK
|
||||||
```
|
```
|
||||||
""" % (lane, operator, area, commit, log,
|
""" % (lane, operator, area, commit, log,
|
||||||
"\n".join("- " + w for w in work), rows, verdict, check, HERE, HERE)
|
"\n".join("- " + w for w in work), rows, verdict, check, RELPATH, RELPATH)
|
||||||
|
|
||||||
|
|
||||||
def stamp_now():
|
def stamp_now():
|
||||||
@@ -187,19 +224,35 @@ def commit_carrying(paths):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(argv=None):
|
||||||
|
argv = list(sys.argv[1:] if argv is None else argv)
|
||||||
path = os.path.join(HERE, LOGNAME)
|
path = os.path.join(HERE, LOGNAME)
|
||||||
stamp = parse_log(open(path, encoding="utf-8").read()).get("stamp") if os.path.exists(path) else None
|
stamp = parse_log(open(path, encoding="utf-8").read()).get("stamp") if os.path.exists(path) else None
|
||||||
|
if "--restamp" in argv:
|
||||||
|
stamp = None # date this rebuild now, deliberately
|
||||||
|
for i, a in enumerate(argv):
|
||||||
|
if a == "--stamp" and i + 1 < len(argv):
|
||||||
|
stamp = argv[i + 1] # an explicit date always wins
|
||||||
if not stamp:
|
if not stamp:
|
||||||
stamp = stamp_now() # first build only; replays reuse the committed stamp
|
stamp = stamp_now() # first build, or an explicit restamp; replays reuse it
|
||||||
|
|
||||||
sha = commit_carrying(["wizard-work/null/wizard.py", "wizard-work/null/check.py",
|
sha = commit_carrying(["wizard-work/null/wizard.py", "wizard-work/null/check.py",
|
||||||
"wizard-work/null/q57_mutation.py"])
|
"wizard-work/null/q57_mutation.py", "wizard-work/null/CHECK"])
|
||||||
if not sha:
|
if not sha:
|
||||||
sha = "unresolved" # the gate refuses to pass on this
|
sha = "unresolved" # the gate refuses to pass on this
|
||||||
|
print("warning: no commit on origin/main carries these bytes yet -- "
|
||||||
|
"commit and push the work files, then re-run this builder")
|
||||||
|
|
||||||
text = render(TASK, AREA, LANE, sha, OPERATOR, stamp)
|
text = render(TASK, AREA, LANE, sha, OPERATOR, stamp)
|
||||||
with open(path, "w", encoding="utf-8") as fh:
|
want = text.encode("utf-8")
|
||||||
fh.write(text)
|
have = open(path, "rb").read() if os.path.exists(path) else b""
|
||||||
|
if have == want:
|
||||||
|
print("unchanged: %s already reads this way (%d bytes, %s)"
|
||||||
|
% (LOGNAME, len(want), sha[:8] or "unresolved"))
|
||||||
|
else:
|
||||||
|
with open(path, "wb") as fh:
|
||||||
|
fh.write(want)
|
||||||
|
print("wrote: %s (%d bytes, %s)" % (LOGNAME, len(want), sha[:8] or "unresolved"))
|
||||||
verdict, check, _ = craft(TASK, AREA, LANE, sha, OPERATOR, stamp)
|
verdict, check, _ = craft(TASK, AREA, LANE, sha, OPERATOR, stamp)
|
||||||
print(verdict)
|
print(verdict)
|
||||||
print(check)
|
print(check)
|
||||||
|
|||||||
Reference in new issue
Block a user