Summary
Three distinct defects share one signature: uncommitted AI checkpoints (the working log) are stranded when Graphite-style plumbing moves the branch, so the next commit mints an empty-attestation note and the pending AI lines blame human. #1976 fixed the forward reset --keep carry (confirmed working — forward controls pass); these are the surviving variants. Isolated by the graphite-workflows scenario suite (#2021), where they drive ~54 failing scenarios across the SYNC_RESTACK / RESTACK / WORKTREE / CONFLICT_CONTINUE families.
Verified present on current main (cb748e61d): the implicated regions are byte-identical to the suite's verification base (src/daemon.rs:5648-5673, src/daemon.rs:5713-5766, src/daemon/ref_cursor.rs:916-931 — ref_cursor.rs has no changes at all since then).
In production (enterprise fleet) this is the "empty note" class: PRs whose every commit carries a zero-attestation note despite heavy captured AI activity; in a single day's audit ~30% of AI-flagged-but-unattributed PRs had this shape.
Defect A — sideways reset -q --keep onto a commit-tree-minted sibling
gt restack's core move for the checked-out branch. The Reset handler renames the working log only for ancestor-related moves (src/daemon.rs:5648-5673); the minted replacement is a sibling, so the flow falls through to handle_non_fast_forward_rewrite, which shifts committed notes but never the working log. Pending checkpoints stay keyed to the now-unreachable old tip.
Minimal repro (traced repo, daemon running):
# 1. AI edit checkpointed while branch tip is O (working log keyed to O)
git-ai checkpoint claude file.txt
# 2. mint a SIBLING of O the way gt restack does (same tree, new parent)
NEW=$(git commit-tree O^{tree} -p <new-parent> -m restacked)
# 3. sideways move (traced)
git reset -q --keep "$NEW"
# 4. commit the still-pending AI lines
git commit -am work
git ai show HEAD # note exists but has ZERO attestations; AI lines blame human
Suite: GT-RESTACK-005 and the SYNC_RESTACK/RESTACK traced mixed/uncommitted clusters.
Defect C — cross-worktree update-ref strands the log even on fast-forwards
The update-ref working-log carry (src/daemon.rs:5713-5766) requires the move to affect the invoking command's HEAD. A branch checked out in a different worktree records no HEAD reflog entry in the invoking worktree, so even plain fast-forward update-ref moves strand its working log.
git worktree add ../wt2 feature # AI checkpoints happen in wt2 (log keyed to feature's tip)
# from the MAIN worktree (as gt does when restacking the whole stack):
git update-ref refs/heads/feature <descendant-sha>
# wt2's pending checkpoints are now stranded; next commit in wt2 -> empty note
Suite: the WORKTREE family's mixed/uncommitted clusters (all shapes).
Defect D — stash pop -q never restores stashed attribution
enrich_stash (src/daemon/ref_cursor.rs:916-931) passes stash_args.get(1) as the stash target without stripping flags, so -q fails target resolution and the attribution saved at stash push (present on disk under .git/ai/stashes_v2/<sha>/INITIAL) is never restored. All existing upstream stash tests are flag-less, which is why this survives.
git-ai checkpoint claude file.txt
git stash push
git stash pop -q # target resolution gets "-q", restore silently skipped
git commit -am work # AI lines blame human
Suite: drives the CONFLICT_CONTINUE clusters (gt uses -q on its stash plumbing).
Fix direction (from the suite triage)
- Carry the working log old→new in the Reset handler's non-ancestor arm (A) and in the update-ref handler for branches checked out in any worktree (C) — mirroring the unconditional rename
apply_checkout_switch_working_log_side_effect already performs for checkouts.
- Skip flags when resolving stash pop targets, defaulting to
stash@{0} (D).
Executable failing tests for all three live in #2021 (tests/integration/graphite_workflows/, taxonomy in EXPECTED_FAILURES.md § Mechanism 2). Related: #1976 (forward fix these variants escape), #2093 / #2099 (sibling gt-plumbing issues on the rewrite/push paths).
Environment: verified on main cb748e61d and enterprise fleet v1.6.15–1.6.20, macOS arm64, git 2.55, Graphite 1.8.x.
Summary
Three distinct defects share one signature: uncommitted AI checkpoints (the working log) are stranded when Graphite-style plumbing moves the branch, so the next commit mints an empty-attestation note and the pending AI lines blame human. #1976 fixed the forward
reset --keepcarry (confirmed working — forward controls pass); these are the surviving variants. Isolated by the graphite-workflows scenario suite (#2021), where they drive ~54 failing scenarios across the SYNC_RESTACK / RESTACK / WORKTREE / CONFLICT_CONTINUE families.Verified present on current
main(cb748e61d): the implicated regions are byte-identical to the suite's verification base (src/daemon.rs:5648-5673,src/daemon.rs:5713-5766,src/daemon/ref_cursor.rs:916-931—ref_cursor.rshas no changes at all since then).In production (enterprise fleet) this is the "empty note" class: PRs whose every commit carries a zero-attestation note despite heavy captured AI activity; in a single day's audit ~30% of AI-flagged-but-unattributed PRs had this shape.
Defect A — sideways
reset -q --keeponto a commit-tree-minted siblinggt restack's core move for the checked-out branch. The Reset handler renames the working log only for ancestor-related moves (src/daemon.rs:5648-5673); the minted replacement is a sibling, so the flow falls through tohandle_non_fast_forward_rewrite, which shifts committed notes but never the working log. Pending checkpoints stay keyed to the now-unreachable old tip.Minimal repro (traced repo, daemon running):
Suite:
GT-RESTACK-005and the SYNC_RESTACK/RESTACK traced mixed/uncommitted clusters.Defect C — cross-worktree
update-refstrands the log even on fast-forwardsThe update-ref working-log carry (
src/daemon.rs:5713-5766) requires the move to affect the invoking command's HEAD. A branch checked out in a different worktree records no HEAD reflog entry in the invoking worktree, so even plain fast-forwardupdate-refmoves strand its working log.Suite: the WORKTREE family's mixed/uncommitted clusters (all shapes).
Defect D —
stash pop -qnever restores stashed attributionenrich_stash(src/daemon/ref_cursor.rs:916-931) passesstash_args.get(1)as the stash target without stripping flags, so-qfails target resolution and the attribution saved atstash push(present on disk under.git/ai/stashes_v2/<sha>/INITIAL) is never restored. All existing upstream stash tests are flag-less, which is why this survives.Suite: drives the CONFLICT_CONTINUE clusters (gt uses
-qon its stash plumbing).Fix direction (from the suite triage)
apply_checkout_switch_working_log_side_effectalready performs for checkouts.stash@{0}(D).Executable failing tests for all three live in #2021 (
tests/integration/graphite_workflows/, taxonomy inEXPECTED_FAILURES.md§ Mechanism 2). Related: #1976 (forward fix these variants escape), #2093 / #2099 (sibling gt-plumbing issues on the rewrite/push paths).Environment: verified on main
cb748e61dand enterprise fleet v1.6.15–1.6.20, macOS arm64, git 2.55, Graphite 1.8.x.