Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6e15d71
Confirmatory runner: lanes, one engine pool per model, admission, the…
MaxGhenis Oct 25, 2026
c04ffe5
Confirmatory runner: tests
MaxGhenis Oct 25, 2026
ec31e83
Confirmatory runner: create both logs at the start
MaxGhenis Oct 25, 2026
808ccc8
Confirmatory runner: a clock that never goes back, and three test-ora…
MaxGhenis Oct 25, 2026
fdbd411
Confirmatory runner: the review's eight runner findings, stop causes,…
MaxGhenis Oct 25, 2026
06abd1f
Confirmatory runner: log excerpts from a day before the run's start
MaxGhenis Oct 25, 2026
f5099db
Confirmatory runner tests: the interrupt test without a timer
MaxGhenis Oct 25, 2026
f210b6e
Confirmatory runner: round-2 fixes, part 1 (not yet tested)
MaxGhenis Oct 25, 2026
36d553a
Confirmatory runner: round-2 fixes, part 2
MaxGhenis Oct 26, 2026
00d267a
Confirmatory runner: round-3 fixes
MaxGhenis Oct 26, 2026
019f842
Confirmatory runner: round-4 fixes
MaxGhenis Oct 26, 2026
9b30a77
Confirmatory runner: round-5 fixes
MaxGhenis Oct 27, 2026
4002f8e
Confirmatory runner: round-6 wording and coverage
MaxGhenis Oct 27, 2026
047c868
Run the no-tools stage with recoverable attempts and cumulative spend.
MaxGhenis Oct 27, 2026
060b287
Confirmatory runner: wait for host-log samples in two timing tests
MaxGhenis Oct 27, 2026
08d7b18
Confirmatory runner: coordinate no-tools shutdown and freeze inputs
MaxGhenis Oct 27, 2026
5a9735b
Confirmatory runner: require standing no-tools finals before calculation
MaxGhenis Oct 27, 2026
c60086b
Confirmatory runner: coordinate interruption and worker cleanup
MaxGhenis Oct 27, 2026
248e777
Confirmatory runner: validate every saved final and baseline inventory
MaxGhenis Oct 27, 2026
4ce7f8e
Confirmatory runner: keep exceptional cleanup signal-safe
MaxGhenis Oct 27, 2026
2f795ea
Confirmatory runner: defer signals and track worker completion
MaxGhenis Oct 28, 2026
6597adb
Confirmatory runner: restore signal handlers before closing the wake …
MaxGhenis Oct 28, 2026
9acc920
Confirmatory runner: let the forced-exit probe wait for the handler
MaxGhenis Oct 28, 2026
a1114fe
Confirmatory runner: cover watcher startup and pipe teardown
MaxGhenis Oct 28, 2026
89dd946
Confirmatory runner: qualify signal and cleanup guarantees
MaxGhenis Oct 28, 2026
4dafb83
Confirmatory runner: refuse undecodable files with their paths
MaxGhenis Oct 28, 2026
040d206
Confirmatory runner: name board and cost refusals
MaxGhenis Oct 28, 2026
6928b06
Confirmatory runner: check the CLI with a real board
MaxGhenis Oct 28, 2026
8b6c8f7
Confirmatory runner: guard board rereads and strict saved costs
MaxGhenis Oct 28, 2026
0300257
Confirmatory runner: align saved treatment and link checks
MaxGhenis Oct 28, 2026
4fb80d9
Confirmatory runner tests: build preflight finals the treatment check…
MaxGhenis Oct 29, 2026
5e334d9
Confirmatory runner: reproduce unreadable evidence refusals
MaxGhenis Oct 29, 2026
d31c1d7
Confirmatory runner: bind decoded inputs and name evidence failures
MaxGhenis Oct 29, 2026
cccc381
Confirmatory runner: guard intermediate stage inventories
MaxGhenis Oct 29, 2026
4d93b40
Confirmatory runner: name the new test modules for what they check
MaxGhenis Oct 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions scripts/confirmatory_anchor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Push the confirmatory run's anchors outside the run.

Usage:
python scripts/confirmatory_anchor.py --repo DIR ANCHORS

The runner calls this every 15 minutes and at its end (with up to three final
tries), with a snapshot of its `anchors.jsonl`: each line holds every log's
line count and chain head at a time. The snapshot is a copy, not a record.
This copies it to `anchors.jsonl` in DIR, a clone checked out on a branch
of its own, commits it and pushes the branch to its remote. The remote's record
of each push is the anchor's time from outside the run: a log edited after an
anchor was pushed no longer matches that anchor's head.

An exclusive non-blocking lock in the clone's `.git` directory covers every
read, copy, commit and push. Every Git command inherits its descriptor, so a
surviving command keeps the clone busy even if this helper alone is killed.
Ordinary local hooks and remote helpers inherit it too. The per-command
`core.fsmonitor=false` disables normal index-triggered fsmonitor startup.
A configured hook can still run `git fsmonitor--daemon start`, whose child
closes inherited descriptors; this falls under the configured-command limit:
configured commands must retain the descriptor without unlocking it in any
background clone writer. This advisory lock does not cover independent
writers or replacement of the lock file. The lock is local: processes on an
external remote server never hold it. The remote's safety rests on the byte-prefix
check and ordinary non-force pushes.

A credential-cache daemon or any other descriptor holder can keep the clone
busy up to the holder's lifetime (the credential cache defaults to 900
seconds), indefinitely if it never exits or closes the descriptor. Every
later try, including all three final tries, then fails as busy and is reported
as a failed push. A busy clone fails without changing its records. Under the
lock, the clone's current file must be a byte prefix of the snapshot (equality
is allowed): a delayed older command cannot replace newer evidence. This
checks bytes, not JSON or line boundaries; the runner supplies complete,
synced lines. Git's own index.lock is left alone; a git failure is a failed
push. Never force-push.
"""

from __future__ import annotations

import argparse
import fcntl
import hashlib
import subprocess
import sys
from pathlib import Path


def git(repo: Path, lock_fd: int, *args: str) -> str:
"""Run Git with the clone lock inherited, retaining failures for the caller."""
return subprocess.run(
["git", "-c", "core.fsmonitor=false", "-C", str(repo), *args],
check=True,
capture_output=True,
text=True,
pass_fds=(lock_fd,),
).stdout


def main(argv=None) -> int:
"""Push a whole snapshot only while exclusively extending the clone's file."""
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
parser.add_argument("--repo", type=Path, required=True, help="the anchors clone")
parser.add_argument("anchors", type=Path, help="a snapshot of the run's anchors.jsonl")
args = parser.parse_args(argv)
repo = args.repo.expanduser()
target = repo / "anchors.jsonl"
with (repo / ".git" / "confirmatory-anchor.lock").open("a") as lock:
try:
fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
print(
"anchor clone busy: another helper or Git process holds its lock",
file=sys.stderr,
)
return 1
snapshot = args.anchors.read_bytes()
current = target.read_bytes() if target.exists() else b""
if not snapshot.startswith(current):
print(
"refusing anchor snapshot: the clone's anchors.jsonl is not a byte "
"prefix of the snapshot",
file=sys.stderr,
)
return 1
if snapshot != current or not target.exists():
target.write_bytes(snapshot)
lines = snapshot.decode().splitlines()
git(repo, lock.fileno(), "add", "anchors.jsonl")
if git(repo, lock.fileno(), "status", "--porcelain", "--", "anchors.jsonl"):
head = hashlib.sha256(lines[-1].encode()).hexdigest() if lines else "none"
git(
repo,
lock.fileno(),
"commit",
"-q",
"-m",
f"anchor {len(lines)}: {head}",
)
git(repo, lock.fileno(), "push", "-q", "origin", "HEAD")
print(f"pushed {len(lines)} anchors")
return 0


if __name__ == "__main__":
sys.exit(main())
Loading