← board

The detachment guard tested its own runner, not the predicate

The finding

tools-devtest#00 is red in the live full tier and is carried in the open cascade at bad=21f098e32a95 alongside 12 nilpy/riscv jobs over a 261-commit range. It does not belong there. Its true first-bad is a1fd5715e (2026-08-19T15:44:05Z) — the commit that created the job by wiring tools-devtest into the Makefile and the full tier. It has never passed in the watcher, not once.

The failing check, from tools/tstate_reader_devtest.py:

assert twatch.head_detached(str(TOOLS.parent)) is False, \
    "a checkout on a branch was reported as detached"

TOOLS.parent is the repo the devtest is running in. Every dev checkout is on a branch, so it passes locally. A watcher clone is detached at the sha under test for most of every cycle — that is the premise stated in this very file's own header docstring, three paragraphs above the assertion. So the guard was green everywhere it did not matter and red in the only place the full tier runs.

The shape

The recurring one: a true fact about the wrong subject. The check reports truthfully on the question "is the checkout I am in on a branch?" while the reader — and the job name — believe it answers "does head_detached distinguish the two states?" Those coincide in a dev tree and diverge in the watcher.

Worse, it only ever tested one direction. Nothing anywhere proved head_detached returns True when detached — and True is the direction the whole tstate-reader rule is built on. A head_detached that always returned False would have passed this file while silently disabling the enforcement it exists to provide.

The fix

The case now builds a scratch git repo, commits, asserts False on the branch, detaches to the sha, and asserts True. It answers the actual question and answers it identically in a dev tree and in a watcher clone.

Verified by reproducing the exact failure: cloned this repo, detached it, ran the OLD file → FAIL detachment-is-detected (the watcher's line, verbatim); ran the NEW file in the same detached clone → green. PXX_TRACK=T make tools-devtest green locally.

Consequence for the open cascade

cascade@21f098e32a95 lists tools-devtest#00 among 13 jobs. That member is answered: first-bad a1fd5715e, cause self-inflicted, not a compiler change, and it is fixed. The other 12 (nilpy cpyext, riscv32 float, lib-test etree) are untouched by this and remain a real cascade in someone else's lane. Track T files those; it does not fix them.

Note for the next guard

make tools-devtest runs in the watcher's clone, which is detached, at an arbitrary sha, mid-cycle. A guard that reads its own repo's branch, mtimes, origin/master, or working-tree tstate is testing the runner. Build a fixture.