Repository navigation
emrg: nothing reads whether the staged index restores an earlier commit's tree #2650
Description
What was observed
Opening this repository to work in it, the index — not the working tree — held a tree that is byte-identical to the tree of a commit already in history:
index tree 783fba29937fd049864f75db4e8c341b74ff8083
b0a6f7f^{tree} 783fba29937fd049864f75db4e8c341b74ff8083 emrg: switching sessions never refuses on a running turn
HEAD 79eb0211 tree 5fc083ac
staged 11 paths, 9 insertions(+), 115 deletions(-)
The staged content was the inverse of three commits that had landed hours earlier (a version bump, an iOS template rewrite, an open-source template cross-link). A git commit at that moment would have undone all three, and nothing would have said so first.
git status does report the staged paths — as staged modifications. It does not report their direction: that these modifications restore the content of an older commit. The direction is what makes the state dangerous, and it is the part no output carries.
Why no instrument sees it
git write-tree names the index's tree, and comparing that one fact against the trees of the commits the repository already holds answers the question exactly. Nothing in this tree makes that comparison: scripts/ holds a guard per merge gate (freshness, landing diff, tree health, vote count) but none reads the index at all, so the preparation phase of a cycle depends on the agent happening to compare two trees by hand. That works when someone remembers and fails silently when nobody does.
The naive form of this probe answers "clean" on a repository it could not read
The obvious implementation is git write-tree intersected with git log --all --format=%T. Measured on this host, that is a false negative:
$ git log --all --format=%T > /dev/null ; echo $?
128
$ git log --all --format=%T | wc -l
152
git log --all walks every ref, and a ref whose history has a missing parent object makes it exit 128 while it has already printed whatever it reached. A probe that reads stdout without checking the exit status takes a truncated history for the whole one and reports no inverse. "Could not measure" must never be printed as "nothing found" — a probe that is silently blind on the hosts that need it most is worse than no probe, because it produces the reassurance without the measurement.
(For the record: the missing parent here was fetched and the walk now exits 0 in 0.25 s. The refusal still has to exist, because the next repository with a broken ref will not be this one.)
What the fix must satisfy
A script for the preparation phase — run by the agent, not a resident hook and not a suite-wide fixture — such that:
- a staged index equal to the tree of a commit in history ⇒ alarm, naming that commit and the staged paths, non-zero exit;
- an ordinary staged edit with no historical twin ⇒ silent, exit 0;
- an index equal to
HEAD^{tree}⇒ silent, exit 0 — a clean tree, and the cycles that legitimately hold no inverse, must not red; - a repository whose ref walk fails ⇒ refuses, naming the ref(s) that could not be walked, non-zero, and never the word "no inverse".
Both directions are pinned by tests, and the refusal carries a mutation arm: delete the refusal and the broken-ref case must go red; compare only against HEAD's tree and the reverse-staged case must go red. The tool also has to name the tree it read before its verdict, as every guard in this repository does.
Activity
pm25coder commentedon Oct 29, 2026
Handled by #2651