Skip to content

Commit 944c87c

Browse files
t3dotggclaude
andcommitted
fix: threads with open PRs no longer auto-settle
An open change request blocks the inactivity auto-settle path in effectiveSettled: a thread with a PR out for review is unfinished work regardless of how quiet it has been. Merge/close still settles immediately, and an explicit user settle still wins. Co-Authored-By: Claude Fable 5 <[email protected]>
1 parent e5c7547 commit 944c87c

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

‎packages/client-runtime/src/state/threadSettled.test.ts‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,15 @@ describe("effectiveSettled", () => {
115115
// Settled iff nothing blocks (pending work / live session) AND
116116
// the override says settled, or (with no override) a merged PR
117117
// or staleness auto-settles. The "active" pin suppresses both
118-
// auto signals.
118+
// auto signals, and an open PR suppresses the inactivity path:
119+
// a thread with a PR out for review is never done, however quiet.
119120
expected:
120121
pending === undefined &&
121122
!running &&
122123
(settledOverride === "settled" ||
123124
(settledOverride === null &&
124-
(changeRequestState === "merged" || inactivity === "stale"))),
125+
(changeRequestState === "merged" ||
126+
(changeRequestState !== "open" && inactivity === "stale")))),
125127
})),
126128
),
127129
),
@@ -176,6 +178,26 @@ describe("effectiveSettled", () => {
176178
}
177179
});
178180

181+
it("never auto-settles a stale thread with an open change request", () => {
182+
const stale = makeShell({ activityAt: STALE });
183+
expect(
184+
effectiveSettled(stale, {
185+
now: NOW,
186+
autoSettleAfterDays: 3,
187+
changeRequestState: "open",
188+
}),
189+
).toBe(false);
190+
// An explicit user settle still wins: open PR only blocks the auto path.
191+
const settled = makeShell({ settledOverride: "settled", activityAt: STALE });
192+
expect(
193+
effectiveSettled(settled, {
194+
now: NOW,
195+
autoSettleAfterDays: 3,
196+
changeRequestState: "open",
197+
}),
198+
).toBe(true);
199+
});
200+
179201
it("keeps an explicitly un-settled merged-PR thread active", () => {
180202
const shell = makeShell({
181203
settledOverride: "active",

‎packages/client-runtime/src/state/threadSettled.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ export function threadWokeAt(
222222
* override. Past the blockers, the explicit user override (thread.settle /
223223
* thread.unsettle commands, projected into settledOverride + settledAt)
224224
* wins in both directions; without one, a thread auto-settles on a
225-
* merged/closed PR immediately or on inactivity past the window. The server
225+
* merged/closed PR immediately or on inactivity past the window — except
226+
* that an open PR blocks the inactivity path entirely. The server
226227
* un-settles on real activity (user message, session start, approval/
227228
* user-input request), so an override never goes stale silently.
228229
*/
@@ -260,6 +261,11 @@ export function effectiveSettled(
260261
if (options.changeRequestState === "merged" || options.changeRequestState === "closed") {
261262
return true;
262263
}
264+
// An open PR is unfinished business regardless of how long the thread has
265+
// been quiet: review can take days, and hiding the thread would bury the
266+
// work waiting on it. Only merge/close (above) or an explicit user settle
267+
// resolves it.
268+
if (options.changeRequestState === "open") return false;
263269
if (options.autoSettleAfterDays === null) return false;
264270

265271
const lastActivityAt = threadLastActivityAt(shell);

0 commit comments

Comments
 (0)