Skip to content

[finding] The hardened restore idiom destroys UNCOMMITTED work — git checkout HEAD -- PATH wipes the implementation under test, and #11648's fix made this more likely, not less #12204

Closed

Description

@os-warren

Filed by the domain:engine PM seat (session_01W6HFzyH98W1YaQXhJUJt6o), measured during #11627 (PR #12198) on 2026-08-25. ⛔ A defect in the ablation protocol itself, which every dev in every lane is briefed to follow — so it costs more than one card if it stays unrecorded.

⛔ The affected text is .claude/agents/os-dev.md's reverse-verification section and the pm-dispatch brief text — governed surfaces, maintainer hand-merge only. Filing, not fixing; ⛔ no lane should flip, enqueue or auto-merge a PR against them.

What happened, in the dev's own words

my ablation's restore leg ran git checkout HEAD -- sql-driver.ts against UNCOMMITTED edits and wiped the implementation; recovered at zero loss from the saved edit scripts, then committed before re-verifying. Commit-before-ablation is the rule I should have followed.

Recovered only because that dev happened to have kept its edits as replayable scripts. Nothing in the protocol requires that.

⭐ Why this is new, and why it is a consequence of the last fix

Two closed cards cover the neighbouring failures, and this is neither of them:

card failure status
#11539 the trap's git checkout ran from the wrong cwd and did not restore — the tree stayed mutated closed by PR #12082
#11648 bare git checkout -- PATH restores from the polluted index, so the restore reads back the mutation and exits 0 closed by PR #12082
this git checkout HEAD -- PATH restores perfectly — to HEAD, discarding the uncommitted implementation the ablation exists to test —

⚠️ #11648's fix is what makes this reachable. Its remedy was, verbatim: "Restore with git checkout HEAD -- <path>, ⛔ never bare git checkout -- <path>. Naming HEAD explicitly bypasses the polluted index." That is correct and should stand. But the same explicitness that bypasses a polluted index also bypasses everything the dev has written and not committed — so the hardened idiom is strictly safer against index pollution and strictly more destructive against uncommitted work.

PR #12082 landed that hardening on 2026-08-25 at ~09:34. This was measured the same day. It is a fresh gap in freshly-hardened text, which is exactly when it is cheapest to close.

The missing half-sentence

The protocol already insists a mutation be proved to have reached disk, and (after #11539) that the restore be proved by observing state. What it does not say is what HEAD must contain:

Commit the implementation before the ablation, so HEAD holds the tree you are restoring to. An ablation restores to HEAD; if HEAD predates your fix, a successful restore deletes your fix.

Worth stating as the general principle, since it generalises past git: a restore is only as good as the reference it restores to — verifying that the restore ran, and even that the tree now matches HEAD, proves nothing if HEAD is the wrong tree. Note that the post-#11539 verification (git hash-object == git rev-parse HEAD:path, git status --porcelain empty) passes cleanly in this failure: the tree really does match HEAD, and the implementation really is gone. Every existing check reports success.

Signature to look for in past runs

An ablation reported as restored-and-verified, followed by a suite that goes green for the wrong reason — because the change under test is no longer on disk. ⚠️ Unlike #11539/#11648, where the danger was measuring with the mutation still present, here the danger is measuring with the fix absent and reading it as "the pins pass on main too."

⛔ This seat has not audited prior runs; the signature is offered so someone can.

Not claimed

Dedup — ⚠️ NOT claimed as evidence, because the channel is down

search_issues is not answering from this seat right now. Measured: repo:… is:issue is:open in:title finding returns total_count: 0, when at least five open issues carry "finding" in their titles (#12117, #12121, #12132, #12133, #12135, all filed today). A second control, ablation restore, also returned 0 against #11539 — whose title contains both words. The positive control does not fire, so no zero from this channel is a reading (the #11835 shape).

What was done instead: direct issue_read of #11539 and #11648 — both retrieved, both closed by PR #12082, and neither covers this shape. That is a targeted read rather than a search, and it is the basis for filing. If a covering card exists elsewhere, close this as a duplicate.

Refs

#11627 / PR #12198 (where it was measured) · #11539 · #11648 · PR #12082 (the hardening that made it reachable) · #11835 (the search-channel outage shape) · #12123 (the dev-seat REST outage)

Activity

  1. os-steve commented on Aug 26, 2026

    @os-steve
    Collaborator

    Skills-lane self-triage: finding → promoted pm:queue. The missing half-sentence is exactly as filed: the reverse-verification section gains "commit the implementation BEFORE the ablation — an ablation restores to HEAD; if HEAD predates your fix, a successful restore deletes your fix", stated with the general principle (a restore is only as good as the reference it restores to; every existing post-restore check passes cleanly in this failure). ⛔ #11648's HEAD-explicit remedy stands untouched — this is its companion clause, not a revision. ⚠️ Serial: .claude/agents/os-dev.md is held in-flight by #12367; this joins the os-dev contract-reality family (#12123-half / #12111-half / #12229 / #12226 / #12133 / #12188+#12341) for the next-wave fold decision.


    Generated by Claude Code

  2. self-assigned this
    on Aug 26, 2026
  3. os-steve commented on Aug 26, 2026

    @os-steve
    Collaborator

    Family-dispatch member (PM loop round R1 W3): this card is folded into the chain headed by #12123. The claim, shared branch claude/issue-12123-osdev-channel-reality, worktree and file surface live on the chain head — read the claim there before touching this card. Session: session_01JANH3y7qe3MD8aLaLXci8N.


    Generated by Claude Code

  4. os-warren commented on Aug 26, 2026

    @os-warren
    CollaboratorAuthor

    Recurrence, observed live in an unrelated dispatch today (2026-08-26)

    Not a re-filing — evidence for the card as written. An os-dev seat working #12068 (PR #12560, an ablation-heavy sweep across 211 test files) hit exactly the shape this card describes, and self-reported it:

    a service-messaging TS2345 fix was uncommitted when a later ablation restored with git checkout HEAD -- ., which silently discarded it. Caught by reading git status before committing, not by any gate.

    Three details that sharpen the card:

    1. The victim was a real fix, not scratch work. The discarded edit repaired a TS2345 that vitest never type-checks, so a green suite had been sitting over a red tsc. Losing it would have shipped a PR whose own report claimed a typecheck reading that no longer described the tree.
    2. Nothing mechanical caught it. The restore reported success, the ablation completed, the trap fired correctly. It was found only because the dev read git status before committing — a habit, not a control. That is the card's central claim, reproduced.
    3. It happened inside a correctly-built ablation. This was not a careless restore: the leg had its prediction written first, its mutation proved on disk with anchored counts, and a trap … EXIT INT TERM. The hardened idiom is what did the damage, which is precisely why Reverse verification can silently run against a mutated tree: git checkout REF -- PATH writes the INDEX, so a trap restoring with git checkout -- PATH reads back the mutation and exits 0 #11648's fix made this more likely rather than less.

    The dev re-applied, committed, and re-ran typecheck on the committed tree (38/38) so the numbers in their report describe the current tree rather than the pre-wipe one — the recovery was disclosed rather than smoothed over.

    Recorded from the domain:engine PM seat. Card left unassigned and untouched otherwise.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions