review: pin review sessions to a single checkpoint in PostCommit · Entire
review: pin review sessions to a single checkpoint in PostCommit
496ee0a→main·
dipree·3w ago·2 files·+81 added/-0 removed
A read-only entire review session touches no files, so it never accrues
shadow-branch content and never reaches the existing FullyCondensed && PhaseEnded terminal path. Left active in the worktree's session set, it was
re-condensed into every subsequent commit's checkpoint — so one review's
session id leaked into many unrelated checkpoints' session lists, and its
prompt rendered once per checkpoint on the session page.
Once a review session has actually been condensed into a checkpoint (gated on handler.condensed, so attribution still gets its one home), mark it ended and fully condensed. PostCommit's existing skip then drops it from all later commits — attach once, then done.
Add TestPostCommit_ReviewSession_PinnedToSingleCheckpoint covering the condense-once-then-skip behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
c8d0a234809eView transcript
[?
Debug Repeated Prompt Review SessionsClaude Code·Opus 4.8·6 steps](/content/gh/entireio/cli/session/fcf246e6-6835-47f9-b232-8ea903defb9b#timeline-c8d0a234809e/index.html)
Changes
2
cmd/entire/cli/strategy
Mmanual_commit_hooks.go+20
Mphase_postcommit_test.go+61
1337 unmodified lines
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1337 unmodified lines
state.FullyCondensed = true
}
// Pin a review session to the single checkpoint it was just condensed into.
// A review is a read-only one-shot: it touches no files, so it never accrues
// shadow-branch content and the FullyCondensed branch above never fires for
// it. Left active, it stays in this worktree's session set and gets
// re-condensed into every subsequent commit — which is how one review ends
// up attributed to many unrelated checkpoints (its prompt rendering once per
// checkpoint on the session page). Once condensed, its transcript is captured
// and it has nothing further to contribute, so mark it terminal here. Gated
// on handler.condensed so it only fires after the review actually landed in a
// checkpoint — a review skipped by the read-only gate is never written to a
// checkpoint and stays eligible to attach to a later one.
if state.Kind.IsReview() && handler.condensed {
if state.EndedAt == nil {
endedAt := time.Now().UTC()
state.EndedAt = &endedAt
}
state.Phase = session.PhaseEnded
state.FullyCondensed = true
}
// State is saved by the outer MutateSessionState in PostCommit.
// Only preserve shadow branch for active sessions that were NOT condensed.
Mcmd/entire/cli/strategy/manual_commit_hooks.go+20
70 unmodified lines
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
70 unmodified lines
"StepCount should be reset after immediate condensation")
}
// TestPostCommit_ReviewSession_PinnedToSingleCheckpoint verifies that a
// read-only review session is marked terminal once it has been condensed into a
// checkpoint, so PostCommit stops re-attaching it to every later commit in the
// worktree. This is the regression guard for the bug where a single `entire
// review` session leaked into many unrelated checkpoints' session lists (its
// prompt then rendering once per checkpoint on the session page). Contrast with
// TestPostCommit_ActiveSession_CondensesImmediately, where a normal ACTIVE
// session is expected to stay ACTIVE.
func TestPostCommit_ReviewSession_PinnedToSingleCheckpoint(t *testing.T) {
dir := setupGitRepo(t)
t.Chdir(dir)
repo, err := git.PlainOpen(dir)
require.NoError(t, err)
s := &ManualCommitStrategy{}
sessionID := "test-postcommit-review"
// Give the review session real shadow-branch content so its first PostCommit
// actually condenses (handler.condensed == true).
setupSessionWithCheckpoint(t, s, repo, dir, sessionID)
// Tag it as an in-flight agent-review session.
state, err := s.loadSessionState(context.Background(), sessionID)
require.NoError(t, err)
now := time.Now()
state.Phase = session.PhaseActive
state.Kind = session.KindAgentReview
state.LastInteractionTime = &now
require.NoError(t, s.saveSessionState(context.Background(), state))
// First commit: the review is condensed into this one checkpoint, then pinned.
commitWithCheckpointTrailer(t, repo, dir, "a1b2c3d4e5f6")
require.NoError(t, s.PostCommit(context.Background()))
state, err = s.loadSessionState(context.Background(), sessionID)
require.NoError(t, err)
require.NotNil(t, state)
assert.Equal(t, session.PhaseEnded, state.Phase,
"review session should be marked ENDED after its single condensation")
assert.True(t, state.FullyCondensed,
"review session should be FullyCondensed so PostCommit skips it on later commits")
require.NotNil(t, state.EndedAt, "review session should have EndedAt stamped")
firstCheckpoint := state.LastCheckpointID
// Second commit (with a genuinely new file so it isn't an empty commit): the
// pinned review session must NOT be re-condensed, i.e. it must not be
// attached to a second checkpoint.
require.NoError(t, os.WriteFile(filepath.Join(dir, "second.txt"), []byte("unrelated change"), 0o644))
commitFilesWithTrailer(t, repo, dir, "b2c3d4e5f6a1", "second.txt")
require.NoError(t, s.PostCommit(context.Background()))
state, err = s.loadSessionState(context.Background(), sessionID)
require.NoError(t, err)
require.NotNil(t, state)
assert.Equal(t, session.PhaseEnded, state.Phase, "review session should stay terminal")
assert.True(t, state.FullyCondensed, "review session should stay FullyCondensed")
assert.Equal(t, firstCheckpoint, state.LastCheckpointID,
"review session must not be condensed into a second checkpoint")
}
// TestPostCommit_IdleSession_Condenses verifies that PostCommit on an IDLE
// session condenses session data and cleans up the shadow branch.
func TestPostCommit_IdleSession_Condenses(t *testing.T) {