Skip to content

fix: fail-open overlap check for ACTIVE sessions in PostCommit#575

Draft
khaong wants to merge 1 commit intomainfrom
fix/active-session-overlap-failopen
Draft

fix: fail-open overlap check for ACTIVE sessions in PostCommit#575
khaong wants to merge 1 commit intomainfrom
fix/active-session-overlap-failopen

Conversation

@khaong
Copy link
Contributor

@khaong khaong commented Mar 2, 2026

Problem

When an agent commits a file mid-turn before SaveStep records it in FilesTouched, the overlap check in shouldCondenseWithOverlapCheck finds no intersection between committed files and FilesTouched, returning false. This orphans the checkpoint trailer added by PrepareCommitMsg — metadata never gets written to entire/checkpoints/v1.

Root cause: Mismatch between PrepareCommitMsg (unconditionally adds trailer for ACTIVE no-TTY sessions) and PostCommit (requires file overlap to condense). FilesTouched is stale mid-turn because it's only updated via SaveStep (Stop hook) or SaveTaskStep.

Observed 3 times in a single session (2335990a): checkpoints 7a17709559b6, 291f49fbcf7b, and 720e4558e8e2 all orphaned.

Fix

When committedTouchedFiles is empty for an ACTIVE session, return true (fail-open) instead of false. This matches the existing fail-open behavior when filesTouchedBefore is empty entirely (line 658-659).

 if len(committedTouchedFiles) == 0 {
-    return false
+    return isActive
 }

Tradeoff

Stale ACTIVE sessions (agent killed without Stop hook) may now be condensed into unrelated commits as a side effect, attaching extra metadata. This is acceptable because orphaned checkpoints (data loss) are worse than extra metadata (data duplication).

Alternatives considered

Approach Why not
Time-based staleness (LastInteractionTime) Arbitrary threshold, fragile
Transcript parsing at PostCommit time Complex, user rejected
Make PrepareCommitMsg smarter Can't reliably attribute commits to sessions
Defer trailer to PostCommit (amend) Amending commits is risky
PostCommit removes orphaned trailers Same amend issues
Track staged files in PrepareCommitMsg Same attribution problem

Discussion points

  1. Is the stale-session false positive acceptable? Extra metadata linked to wrong commit vs. no metadata at all.
  2. Should we explore a hybrid approach? e.g., fail-open for ACTIVE but add a heuristic to reduce false positives later.
  3. PrepareCommitMsg/PostCommit contract: Should we formalize the expectation that PostCommit always honors trailers from ACTIVE sessions?

Test plan

  • TestPostCommit_ActiveSession_CommitsFileNotInFilesTouched_Condenses — new test for the bug
  • TestPostCommit_StaleActiveSession_CondensedAlongside — updated: stale session now condensed (was _NotCondensed)
  • Full test suite passes (mise run fmt && mise run lint && mise run test:ci)

When an agent commits a file mid-turn before SaveStep records it in
FilesTouched, the overlap check in shouldCondenseWithOverlapCheck found
no intersection between committed files and FilesTouched, returning
false. This orphaned the checkpoint trailer added by PrepareCommitMsg
(no matching metadata on entire/checkpoints/v1).

The fix: when committedTouchedFiles is empty for an ACTIVE session,
return true (fail-open) instead of false. This matches the existing
fail-open behavior when filesTouchedBefore is empty entirely.

Tradeoff: stale ACTIVE sessions (agent killed without Stop hook) may
now be condensed into unrelated commits as a side effect. This is
acceptable because orphaned checkpoints (data loss) are worse than
extra metadata (data duplication).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b1b803dfc6d4
Copilot AI review requested due to automatic review settings March 2, 2026 10:13
@cursor
Copy link

cursor bot commented Mar 2, 2026

PR Summary

Medium Risk
Changes PostCommit condensation behavior for ACTIVE sessions by allowing condensation even when file overlap cannot be proven, which can attach checkpoint metadata to unrelated commits for stale sessions. It reduces risk of orphaned checkpoint trailers (missing metadata) when FilesTouched is stale mid-turn.

Overview
PostCommit condensation now fails open for ACTIVE sessions when overlap can’t be confirmed. Specifically, shouldCondenseWithOverlapCheck returns true for ACTIVE sessions even when committedTouchedFiles is empty, preventing checkpoint trailers added by PrepareCommitMsg from being orphaned.

Tests were updated/added to lock in the new tradeoff: a stale ACTIVE session may be condensed alongside an unrelated commit, and a new regression test covers the mid-turn case where an agent commits a file not yet present in FilesTouched.

Written by Cursor Bugbot for commit 19de42a. Configure here.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a PostCommit condensation edge case where ACTIVE sessions could leave orphaned Entire-Checkpoint trailers when the committed files don’t intersect with FilesTouched (common when an agent commits mid-turn before SaveStep updates state). This aligns PostCommit behavior with PrepareCommitMsg’s trailer behavior to avoid losing checkpoint metadata.

Changes:

  • Adjust shouldCondenseWithOverlapCheck to fail-open for ACTIVE sessions when committedTouchedFiles is empty.
  • Update the stale ACTIVE-session test expectations to reflect the new fail-open behavior.
  • Add a regression test covering the “commit mid-turn before FilesTouched is updated” scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cmd/entire/cli/strategy/manual_commit_hooks.go Implements ACTIVE fail-open when overlap can’t be confirmed because no touched files were committed.
cmd/entire/cli/strategy/phase_postcommit_test.go Updates stale-session behavior expectations and adds a regression test for the orphaned-trailer scenario.

Comment on lines +1788 to +1795
// StepCount is 1 after condensation because carry-forward writes remaining
// files to the new shadow branch (condensation resets to 0, then carry-forward sets to 1).
assert.Equal(t, 1, staleState.StepCount,
"Stale ACTIVE session StepCount should be 1 (condensed + carry-forward)")

// BaseCommit IS updated for ACTIVE sessions (updateBaseCommitIfChanged)
// BaseCommit updated to new HEAD
assert.Equal(t, newHead, staleState.BaseCommit,
"Stale ACTIVE session BaseCommit should be updated (ACTIVE sessions always get BaseCommit updated)")
assert.NotEqual(t, staleOriginalBaseCommit, staleState.BaseCommit,
"Stale ACTIVE session BaseCommit should have changed")
"Stale ACTIVE session BaseCommit should be updated after condensation")
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, the assertions used to prove the stale ACTIVE session was condensed are ambiguous: StepCount == 1 is already true immediately after setupSessionWithCheckpoint() (SaveStep increments it), and BaseCommit == newHead can also happen via updateBaseCommitIfChanged even when no condensation occurs. Consider asserting a field that only changes on condensation (e.g., AttributionBaseCommit should update to newHead, and/or PromptAttributions should be cleared) so the test actually fails if the stale session is not condensed.

Copilot uses AI. Check for mistakes.
Comment on lines +637 to +647
// shouldCondenseWithOverlapCheck returns true if the session should be condensed
// into this commit. Requires both that hasNew is true AND that the session's files
// overlap with the committed files with matching content.
//
// This prevents stale sessions (ACTIVE sessions where the agent was killed, or
// ENDED/IDLE sessions with carry-forward files) from being condensed into every
// unrelated commit.
// This prevents IDLE/ENDED sessions with carry-forward files from being condensed
// into every unrelated commit.
//
// For ACTIVE sessions, we fail-open when file overlap cannot be confirmed. This is
// because the agent may commit files mid-turn before SaveStep records them in
// FilesTouched, causing the overlap check to see stale data. Failing closed in this
// case orphans the checkpoint trailer (metadata never written to entire/checkpoints/v1).
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function comment says condensation "Requires ... that the session's files overlap with the committed files with matching content", but the implementation now explicitly returns true for ACTIVE sessions when committedTouchedFiles is empty (fail-open) and therefore does not require overlap/content matching in that case. Please adjust the wording to reflect the ACTIVE fail-open exception so the comment matches behavior.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants