Skip to content

emrg: nothing reads whether the staged index restores an earlier commit's tree #2650

Closed

Description

@pm25coder

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

  1. pm25coder commented on Oct 29, 2026

    @pm25coder
    CollaboratorAuthor

    Handled by #2651

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions