← board

sync.sh pulls under a running sweep, and the commit that does it is a ticket

The rule already exists — CLAUDE.md, "PUSH BEFORE A MEASUREMENT STARTS, NEVER DURING ONE", added the same day. It was walked into twice anyway, by two seats who could both quote it, which makes this a placement and enforcement problem rather than a knowledge one.

Why prose is losing here, in frankB's words: "I did not think of a ticket commit as touching the instrument. Landing a fix during a sweep is obviously wrong and I would not have done it; pushing a .md felt like paperwork. sync.sh does not know the difference — the pull is the same pull." The rule as carried is about CONTENT (do not edit code) and the correct form is about the COMMAND: any sync.sh, any pull, for any reason, is touching the instrument. And the rule is filed under "push often" — under the act, where nobody about to start a measurement is reading.

The tell exists and is nearly always missed. frankB's run gave rc 2, which the handbook records as the shape no test in the harness can produce. They read the failing ROW instead of the exit code, and the failing row had a story: their own edit, in the right file, with a plausible mechanism. A red with a good explanation is the one to check hardest.

The guard that was written, and why it is not in the tree

Insert before the fetch in tools/sync.sh; refuse with a documented escape (PXX_SYNC_DURING_SWEEP=1), the pattern no-full-suite.sh already uses.

sweep_running_here() {
    for _d in /proc/[0-9]*; do
        _p=${_d#/proc/}
        [ "$_p" = "$$" ] && continue
        [ "$(readlink "$_d/cwd" 2>/dev/null)" = "$ROOT" ] || continue
        _a=$(tr '\0' ' ' < "$_d/cmdline" 2>/dev/null) || continue
        case "$_a" in
            "bash tools/gate.sh"*|"sh tools/gate.sh"*|tools/gate.sh*) echo "$_p $_a"; return 0 ;;
            *testmgr*--tier*) echo "$_p $_a"; return 0 ;;
        esac
    done
    return 1
}

Both halves of the predicate are deliberate and both are the day's lessons:

IT IS NOT SHIPPED BECAUSE ITS NEGATIVE CONTROL FIRED. With no sweep running, the detector still returned a hit — it matched the testing shell's own command line, which contained the case patterns as literal text. A guard against instrument self-matching, whose own test self-matched. The positive control (a process with argv[0] = bash tools/gate.sh quick and cwd = $ROOT) fired correctly, so the detector can fire; what is unproven is that it stays silent when it should.

Shipping it would have put a false refusal in every seat's inner loop — far worse than the defect, and sync.sh is the one script every lane runs. Reverted rather than tuned, because a guard nobody trusts gets an env var permanently exported and then it is not a guard.

Whoever takes this: the deliverable is a NEGATIVE control that passes. The guard is easy; the control is the work.

THE DETECTOR IS SOLVED, AND THE PROPERTY IS NOT THE ONE I WAS AIMING AT (frankB, 2026-09-06)

Read a field the waiter CANNOT WRITE INTO. argv is writable by every waiter — that is the whole disease — and comm is not.

pgrep -f 'make test'   ->  5 pids     (1 real make, 3 orphan waiters, 1 my own probing shell)
pgrep -x make          ->  1 pid      comm=make

pgrep -x matches the executable NAME, so a shell running pgrep -x make cannot match its own probe — its command name is bash whatever its arguments say. Verified here: the probing shell does not appear in its own result. That is a negative control that holds structurally rather than by luck, which is exactly what this ticket was blocked on.

AND IT KILLS THE OBVIOUS FIX FIRST. The bracket trick does nothing:

pgrep -f 'make test'    ->  143678 363689 1655102 1772877 1772931 1906270
pgrep -f '[m]ake test'  ->  143678 363689 1655102 1772877 1772931 1906270

Identical. [m]ake works against ps | grep because grep's own argv carries the pattern; pgrep never puts its pattern into the matched processes' argv at all. The trick defends the tool against itself and does nothing about third parties, and third parties are the actual failure mode.

THE ORPHANS ARE THE DURABLE HALF. Measured on this box: three bash processes matching pgrep -f 'make test', each an until ! pgrep -f "make test" loop whose own argv contains the string it waits to disappear — so each can never exit, and while it lives it makes every other pgrep -f 'make test' in that session answer YES. Ages when measured: 103275s (28.7 hours) in one checkout, 853s in another, and 0s in a third — that last one being this ticket author's own probing shell, matching itself while measuring the defect.

A guard that is merely wrong fails once. A self-perpetuating waiter makes every future check in that session wrong, silently, and outlives the thing it was watching by a day. Nothing cleans them up.

Two caveats so the guard is not aimed wrong:

And the corollary for every waiter in the fleet, which is the wider fix: wait on a PID, not on a stringwhile kill -0 $pid 2>/dev/null; do sleep 5; done. A pid cannot match itself.