Repository navigation
v0.4.0
Every finding of the round-2 review was checked in the code first; all were real and are fixed here.
Fixed after the round-3 review (each reproduced by a failing test first)
- Malformed policy. A
gate.tomlwith a TOML syntax error, a value of the wrong type, or a[[tests]]
entry withoutcommandcrashed with a traceback (the missing command only whenqueuereached it). The
policy is now validated when it is read; the error names the file, the line or key, and how to fix it. - Forged provenance trailers.
--model,--tool,--specand--titlewent into the squash commit
message verbatim, so a value containing a newline could add a fakeAgent-Head:trailer and make crash
recovery mark another queued session as merged.submitnow rejects multi-line values, and every value is
flattened to one line when the commit is written. - verify and merge commits. A merge commit on the target that carried an
Agent-Sessiontrailer could be
picked as the culprit and auto-reverted against its first parent, undoing a whole branch. verify now stops
with exit 3 for any merge-commit culprit. - Misspelled policy keys. An unknown key (for example
test_comand) was ignored, so the default stayed in
force, which fortest_commandmeans merging untested. Unknown keys are now refused, with a suggestion. - Duplicate suite names. Two
[[tests]]suites with one name shared a pass-cache key, so one suite's pass
could skip the other. Duplicate names (includingdefault, the name oftest_command) are refused. - Kill while testing. The crash-safety row "killed while testing a batch" is now proven with a real process
kill between the green test run and publication (GATE_FAULT=after-tests): nothing is published, the next
run re-lands the batch once.
Fixed during the round-2 review
- Locks across hosts. Staleness compared a lock file's mtime with the local clock, so clock skew between
hosts (or NFS attribute caching) could make a live lock look abandoned. Liveness is now judged by observation:
a lock is taken over only after this process has watched it stay unchanged forLOCK_STALE_Son its own
monotonic clock (or its owner is a dead pid on this host, or it is older than 10xLOCK_STALE_S). Lock files
are read with open() + fstat() so NFS close-to-open consistency applies. Proven on a real NFS export with two
hosts and a 120 s clock skew (nfsworkflow). - Protected branches. A push refused by branch protection or a hook was treated as contention: the batch
was re-tested five times and the run ended with a misleading "target kept moving". Only lease failures count
as contention now; any other refusal stops at once with the remote's message and what to do, sessions stay
queued, tests run once. - Checked-out target. Only
queuerefused to run while the target branch was checked out;sync_tipand
advance(remote fetch, verify revert) could still move it under a worktree. Both now refuse. - Crash during verify. A verify killed after publishing a revert never marked the session
reverted. The
next verify reconciles from theReverts:/Agent-Session:trailers (result.recovered). - gc vs resubmit. gc abandoned rejected sessions from a snapshot taken without the state lock, so a session
fixed and resubmitted meanwhile could lose its branch. Abandon is now a compare-and-set on head and status. - Atomic writes. Traces and archived patches are written via temp file + rename like all other state.
- External scanner. A scanner that hangs or cannot start is a finding (fail closed) instead of a crash;
limitSCAN_TIMEOUT_S= 300 s. - merge-tree errors. A merge-tree failure on a bad head was reported as an empty conflict; it is now an
error naming the session. - Windows lock breaking. Watching a stale-lock breaker file kept a handle open that stopped its owner
deleting it (found by CI). The breaker is observed with stat(), and unlinks retry briefly. - README encoding: the 0.3.0 README had a byte-order mark and mojibake from an editor round trip.
Added
--jsonis accepted after the subcommand as well (gate queue --json).gate completion bash|zsh|fish, generated from the parser.gate doctor --fixdetaches a clean main checkout that has the target checked out.GATE_LOCK_STALE_Senvironment variable (default 60).- Tests that kill the real process (SIGKILL / TerminateProcess) after a publish and after a revert publish,
instead of raising an exception in-process; tests for skewed and silent foreign locks, a protected remote,
the checked-out guard, gc racing a resubmit, atomic writes, scanner timeout, merge-tree failure, verify with no
green baseline, completion scripts, doctor --fix, and clean UTF-8 in every tracked text file. nfs_e2e.py+nfsworkflow: two docker hosts, each its own NFS client, one clock 120 s ahead; live lock
kept, dead host's lock taken over afterLOCK_STALE_S, both hosts draining at once land every session once.e2e_github.py: the crash scenario now kills the real process; newprotected(branch protection with
admins enforced, then the documented bypass) andthroughput(ten publishes, lock-hold median/p95) scenarios.bench.py --test-seconds-max(per-candidate suite time drawn from a seeded uniform range) and--modes.