You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I searched existing issues and did not find a duplicate.
I included enough detail to reproduce or investigate the problem.
Area
apps/server
Summary
Since #8600 moved settlement server-side, inactivity auto-settle no longer applies to any thread whose branch resolves to an open pull request. Those threads stay in the sidebar forever regardless of the configured threshold. Before #8600 they settled normally, so on upgrade a batch of long-quiet threads reappears in the active list.
On this machine that is 29 threads, all inactive for more than 30 days, against a configured threshold of 7 days.
Steps to reproduce
Set Auto settle inactive threads to any threshold (7 days here).
Have several threads on a long-lived feature branch that has an open PR/MR — one that stays open for weeks (long review cycle, draft MR, stacked work).
Let those threads go quiet for well past the threshold. On a build before 0.0.38-nightly.20260901.1246 they classify as settled and collapse into the settled shelf.
Threads inactive past the configured threshold settle, as they did before #8600.
Actual behavior
Every thread on a branch with an open PR stays in the active list indefinitely. The sidebar fills with months-old threads and the inactivity threshold has no effect on them.
The rule itself is intentional and is not new — resolveAutoSettlementAt returns null for an open PR at any age:
What changed is whether the PR state was ever known for these threads. The old client-side effectiveSettled carried the identical guard, but it only fired when changeRequest was actually supplied, and that came from threadChangeRequestSnapshotsAtom — a session-lifetime map populated from live VCS status only by sidebar rows that mounted and rendered:
Threads sitting in the collapsed settled shelf never published a snapshot, so changeRequest resolved to null, the open-PR guard never fired, and they aged out on the threshold — and once settled and collapsed they stayed that way. ThreadSettlementReactor now resolves the branch→PR authoritatively for every candidate on every sweep, so the guard fires for all of them.
So the old behavior was arguably wrong (deciding on data the client did not have), but the user-visible effect of correcting it is that the inactivity threshold silently stopped applying to an entire class of threads, with no UI indicating why.
Diagnosis
The settlement reactor is healthy — this is not a sweep failure. From server.trace.ndjson across all 11 rotations:
ThreadSettlementReactor.sweep — 5 spans, all Success
ThreadSettlementReactor.pullRequestFor — 35 spans, all Success
Thread state in projection_threads (non-archived, non-deleted):
settled_override
count
settled
794
active
1
null
32
29 of those 32 have last activity more than 30 days old. All 29 have pending_approval_count = 0, pending_user_input_count = 0, no snooze, session stopped or ready, and no linked_pull_request_json — nothing blocks them in isAutoSettlementCandidate. They collapse onto just five branches:
Threads
Branch
Forge
PR/MR state
12
branch A
self-hosted GitLab
open
7
branch B
self-hosted GitLab
open (draft)
4
branch C
GitHub
open
3
branch D
GitHub
open
3
branch E
GitHub
open
Every one verified open against its forge. The 5 lookup groups match the ~7 pullRequestFor spans per sweep. The sweep settled 794 threads correctly; these 29 are refused solely by the open-PR guard.
sidebarAutoSettleOnMerge is false here, but that is not a factor — a merged PR falls through to the inactivity check; only open hard-blocks.
Impact
Minor bug or occasional failure
Version or commit
0.0.39-nightly.20260903.1273 (03728361aa7beb9c13da320097450e6fe65aac3e). Regression introduced by #8600, first shipped in 0.0.38-nightly.20260901.1246.
Environment
Linux x64 (Ubuntu 20.04), Node v24.15.0, self-hosted server (npx t3) + web client. Repos on GitHub and self-hosted GitLab.
Logs or stack traces
# settings.json"sidebarAutoSettleAfterDays": 7
"sidebarAutoSettleOnMerge": false# server.trace.ndjson* — no settlement warnings at all
$ grep -c 'automatic thread settlement skipped' server.trace.ndjson* -> 0 in every rotation
$ grep -c 'automatic thread settlement sweep failed' server.trace.ndjson* -> 0 in every rotation
# ThreadSettlementReactor spans, current rotation
ThreadSettlementReactor.sweep 5 exit=Success (~2.8s each)
ThreadSettlementReactor.pullRequestFor 35 exit=Success
# projection_threads, non-archived non-deleted, settled_override is null# and last activity older than 30 days
count = 29
all: pending_approval_count=0 pending_user_input_count=0 snoozed_until=NULL
linked_pull_request_json=NULL session in (stopped, ready)
distinct (branch, workspace_root) = 5, every one with an open PR/MR
Workaround
Settle the affected threads manually. That sticks, because settledOverride is now persisted and excludes them from later sweeps.
Suggested direction
Some way for inactivity to eventually win over an open PR. Options, roughly in order of how small they are:
Let inactivity settle past some multiple of the threshold even with an open PR — an untouched thread on a PR that has itself been idle for a month is not live work.
Anchor the open-PR guard to the PR'supdatedAt rather than treating any open PR as unfinished business, so an abandoned PR stops pinning its threads.
Make the open-PR guard a separate setting, so people with long-lived branches can opt out.
Happy to send a PR if you have a preference on the shape.
Before submitting
Area
apps/server
Summary
Since #8600 moved settlement server-side, inactivity auto-settle no longer applies to any thread whose branch resolves to an open pull request. Those threads stay in the sidebar forever regardless of the configured threshold. Before #8600 they settled normally, so on upgrade a batch of long-quiet threads reappears in the active list.
On this machine that is 29 threads, all inactive for more than 30 days, against a configured threshold of 7 days.
Steps to reproduce
Auto settle inactive threadsto any threshold (7 days here).0.0.38-nightly.20260901.1246they classify as settled and collapse into the settled shelf.Expected behavior
Threads inactive past the configured threshold settle, as they did before #8600.
Actual behavior
Every thread on a branch with an open PR stays in the active list indefinitely. The sidebar fills with months-old threads and the inactivity threshold has no effect on them.
The rule itself is intentional and is not new —
resolveAutoSettlementAtreturnsnullfor an open PR at any age:t3code/apps/server/src/orchestration/ThreadSettlementPolicy.ts
Lines 80 to 85 in 0372836
What changed is whether the PR state was ever known for these threads. The old client-side
effectiveSettledcarried the identical guard, but it only fired whenchangeRequestwas actually supplied, and that came fromthreadChangeRequestSnapshotsAtom— a session-lifetime map populated from live VCS status only by sidebar rows that mounted and rendered:t3code/apps/web/src/components/Sidebar.tsx
Lines 2110 to 2120 in 7e4ce3b
Threads sitting in the collapsed settled shelf never published a snapshot, so
changeRequestresolved tonull, the open-PR guard never fired, and they aged out on the threshold — and once settled and collapsed they stayed that way.ThreadSettlementReactornow resolves the branch→PR authoritatively for every candidate on every sweep, so the guard fires for all of them.So the old behavior was arguably wrong (deciding on data the client did not have), but the user-visible effect of correcting it is that the inactivity threshold silently stopped applying to an entire class of threads, with no UI indicating why.
Diagnosis
The settlement reactor is healthy — this is not a sweep failure. From
server.trace.ndjsonacross all 11 rotations:automatic thread settlement skipped— 0 occurrencesautomatic thread settlement sweep failed— 0 occurrencesThreadSettlementReactor.sweep— 5 spans, allSuccessThreadSettlementReactor.pullRequestFor— 35 spans, allSuccessThread state in
projection_threads(non-archived, non-deleted):settled_overridesettledactivenull29 of those 32 have last activity more than 30 days old. All 29 have
pending_approval_count = 0,pending_user_input_count = 0, no snooze, sessionstoppedorready, and nolinked_pull_request_json— nothing blocks them inisAutoSettlementCandidate. They collapse onto just five branches:Every one verified open against its forge. The 5 lookup groups match the ~7
pullRequestForspans per sweep. The sweep settled 794 threads correctly; these 29 are refused solely by the open-PR guard.sidebarAutoSettleOnMergeisfalsehere, but that is not a factor — a merged PR falls through to the inactivity check; onlyopenhard-blocks.Impact
Minor bug or occasional failure
Version or commit
0.0.39-nightly.20260903.1273(03728361aa7beb9c13da320097450e6fe65aac3e). Regression introduced by #8600, first shipped in0.0.38-nightly.20260901.1246.Environment
Linux x64 (Ubuntu 20.04), Node v24.15.0, self-hosted server (
npx t3) + web client. Repos on GitHub and self-hosted GitLab.Logs or stack traces
Workaround
Settle the affected threads manually. That sticks, because
settledOverrideis now persisted and excludes them from later sweeps.Suggested direction
Some way for inactivity to eventually win over an open PR. Options, roughly in order of how small they are:
updatedAtrather than treating any open PR as unfinished business, so an abandoned PR stops pinning its threads.Happy to send a PR if you have a preference on the shape.