Skip to content

fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR#4309

Open
t3dotgg wants to merge 2 commits into
mainfrom
t3code/unsettle-thread-on-message
Open

fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR#4309
t3dotgg wants to merge 2 commits into
mainfrom
t3code/unsettle-thread-on-message

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 22, 2026

Copy link
Copy Markdown
Member

Problem

Sending a message in a settled thread should un-settle it. Two of the three settle signals already behaved:

  • Explicit override — the server decider prepends thread.unsettled when thread.turn.start hits an overridden thread (decider.ts).
  • Inactivity auto-settle — the new message refreshes threadLastActivityAt.

But the merged/closed-PR auto-settle signal in effectiveSettled is client-derived and never clears. Send a message in a merged-PR thread and the row un-settles only while the queued-turn grace / live-session blockers hold — the moment the turn completes, changeRequestState === "merged" classifies it right back into the settled tail. This also defeats the server's un-settle on explicitly settled threads whose PR is merged: the override clears on send, then the merge signal re-settles the row anyway.

Fix

Gate the change-request signal on idleness: a merged/closed PR settles a thread only after it has been quiet for CHANGE_REQUEST_SETTLE_IDLE_MS (1 hour). Fresh activity — the sent message, its turn starting and completing — keeps the follow-up conversation in the active list; once the burst goes stale, the merge signal settles it again. This mirrors the decider's own "activity clears overrides so the thread can auto-settle again after this burst of work goes stale" model.

Web (SidebarV2.tsx) and mobile (threadListV2.ts) both partition through the shared effectiveSettled, so one change covers both clients. No contract or server changes: the VCS status contract carries no merge timestamp, so idle-based gating is the clean option.

The explicit Settle action on a warm merged-PR thread still works — a user settle sets settledOverride, which is checked before the merge signal.

Testing

  • New test: a warm thread (recent activity) stays active under both merged and closed PR states; an idle one still settles.
  • Full client-runtime suite passes (449 tests), mobile threadListV2 tests pass, lint and typecheck clean.

🤖 Generated with Claude Code


Note

Low Risk
Client-only list-partitioning logic in shared effectiveSettled; no auth, server, or contract changes—worst case is threads appearing in the wrong list briefly.

Overview
Merged or closed change requests no longer force a thread into the settled list while it still has recent activity. effectiveSettled now requires at least CHANGE_REQUEST_SETTLE_IDLE_MS (one hour) since threadLastActivityAt before the merge/closed signal can auto-settle; otherwise the thread stays active.

This stops follow-up messages in merged-PR threads from un-settling only until the turn finishes, then snapping back into the settled tail because the PR state never clears. After the burst goes idle past the hour window, auto-settle on merge/closed behaves as before. Explicit Settle and other blockers are unchanged.

Tests cover warm vs idle boundaries (including activity exactly one hour old) and re-settling once the idle window elapses.

Reviewed by Cursor Bugbot for commit ca5b817. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Keep warm threads un-settled on merged/closed PRs until idle for 1 hour

Previously, effectiveSettled in threadSettled.ts would immediately settle a thread when a change request was merged or closed. Now it only settles if the thread has had no activity in the last 60 minutes (CHANGE_REQUEST_SETTLE_IDLE_MS), preserving threads with recent follow-up activity.

  • Behavioral Change: merged/closed PRs no longer auto-settle threads instantly; threads remain active for up to 1 hour after the last activity.

Macroscope summarized ca5b817.

…losed PR

Sending a message in a settled thread un-settles it server-side (the
decider prepends thread.unsettled on turn.start), but the merged/closed-PR
auto-settle signal in effectiveSettled is client-derived and never clears,
so the row snapped back into the settled tail the moment the new turn
completed.

Gate the change-request signal on thread idleness: a merged/closed PR only
settles a thread quiet for at least an hour. Fresh activity keeps the
follow-up conversation in the active list; once the burst goes stale the
merge signal settles it again, matching the decider's activity-clears-
overrides model. Web and mobile both partition through the shared
effectiveSettled, so this covers both clients.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ab9ac02c-c8b6-4070-919a-828c8d194e82

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/unsettle-thread-on-message

Comment @coderabbitai help to get the list of available commands.

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 22, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

Straightforward bug fix with clear intent and limited scope. The change adds an idle-window check (1 hour) before settling threads on PR merge/close signals, preventing a race condition where fresh activity would immediately re-settle. Well-documented and properly tested.

You can customize Macroscope's approvability policy. Learn more.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Jul 22, 2026
…le; note clock-skew exposure

Codex review follow-ups: assert the strict one-hour boundary (activity
exactly an hour old stays warm), assert the same shell re-settles once the
follow-up burst goes idle, and document that cross-device clock skew
shifts the idle window by its size — the same accepted exposure as the
inactivity auto-settle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review July 22, 2026 21:14

Dismissing prior approval to re-evaluate ca5b817

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant