fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(settings): respect completed pull request auto-settlement preference - #2

Merged
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting
Aug 13, 2026
Merged

fix(settings): respect completed pull request auto-settlement preference#2
Defmon3 merged 10 commits into
customfrom
pr/completed-pr-auto-settle-setting

Conversation

@Defmon3

@Defmon3Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Completed pull requests currently settle their associated threads regardless of the user's inactivity preference. This makes the existing auto-settle controls incomplete: users cannot keep completed-PR threads active without manually restoring them.

This change adds a dedicated Auto-settle completed pull requests preference across the shared settings contract, web/desktop settings, and mobile settings. The completed-PR settlement path now respects that preference while explicit settlement and inactivity-based settlement retain their existing behavior. Existing installations decode the new preference as enabled, preserving current behavior until a user turns it off.

The mobile and web consumers share the same settlement decision semantics, preference loading avoids a transient incorrect state, settings reset/search behavior is covered, and the user documentation explains how the two automatic settlement controls interact.

Validation:

  • 9 focused test files: 297 tests passed
  • Typecheck passed for @t3tools/contracts, @t3tools/client-runtime, @t3tools/web, @t3tools/desktop, and @t3tools/mobile
  • Scoped lint passed for all 26 changed TypeScript/TSX files
  • Formatting passed for all 27 changed files
  • git diff --check origin/custom...HEAD passed
  • Two independent read-only reviews reported zero findings on the reviewed implementation
  • Web runtime verification exercised default-on settlement, both automatic controls disabled, and explicit manual settlement

Implemented and verified with GPT-5.6 Terra coding agents and GPT-5.6 Sol reviewers through the Codex harness in T3 Code.

Summary by Sourcery

Add a dedicated preference for auto-settling threads on completed pull requests and wire it through contracts, clients, and settlement logic so completed-PR auto-settlement can be independently controlled from inactivity-based auto-settlement.

New Features:

  • Introduce a sidebar auto-settle completed pull requests setting in the shared client settings schema, with a default-on behavior for existing installations.
  • Expose an Auto-settle completed pull requests toggle in web/desktop general settings and mobile general settings for the flat thread list.

Enhancements:

  • Update thread settlement logic to honor the completed-PR auto-settle preference while keeping inactivity-based settlement and manual overrides unchanged.
  • Ensure mobile uses a device-local completed-PR auto-settle preference, including hydration-aware resolution and conditional display of the control based on list mode.
  • Extend settings search, reset, and navigation on web to account for the new completed-PR auto-settle setting, including legacy-sidebar-aware filtering.

Documentation:

  • Expand thread sidebar user documentation to explain how inactivity and completed-pull-request auto-settlement interact across web, desktop, and mobile.

Tests:

  • Add and update tests across contracts, client runtime, web, desktop, and mobile to cover default decoding, persistence, settings search/restore behavior, and settlement decisions for various PR states and preference combinations.
  • Introduce mobile-specific tests to validate preference sanitization, visibility of the completed-PR auto-settle control, and flat-list settlement behavior under different toggle states.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 13, 2026
@sourcery-ai

sourcery-aiBot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a user-controllable “auto-settle completed pull requests” preference across shared contracts, web/desktop settings, and mobile settings, wires it into the thread settlement logic, and updates mobile/web UI, persistence, search, restore, and docs so completed-PR settlement respects this toggle while maintaining existing inactivity and manual settlement semantics.

Sequence diagram for thread settlement with completed-PR auto-settle toggle

sequenceDiagram
participant Sidebar
participant ClientSettings
participant ThreadShell
participant effectiveSettled
Sidebar->>ClientSettings: get sidebarAutoSettleAfterDays
Sidebar->>ClientSettings: get sidebarAutoSettleCompletedChangeRequests
Sidebar->>ThreadShell: read changeRequestState
Sidebar->>effectiveSettled: effectiveSettled(shell,{now,autoSettleAfterDays,autoSettleCompletedChangeRequests,changeRequestState})
alt autoSettleCompletedChangeRequests && changeRequestState in [merged, closed]
effectiveSettled-->>Sidebar: true (auto-settle by completed PR)
else changeRequestState == open
effectiveSettled-->>Sidebar: false (open PR blocks inactivity settle)
else settledOverride == "settled"
effectiveSettled-->>Sidebar: true (explicit settle wins)
else settledOverride == "active"
effectiveSettled-->>Sidebar: false (explicit keep-active wins)
else inactivity window passed
effectiveSettled-->>Sidebar: true (auto-settle by inactivity)
else
effectiveSettled-->>Sidebar: false (keep active)
end
Loading

File-Level Changes

ChangeDetailsFiles
Introduce a shared completed-pull-request auto-settle setting in the contracts and client settings APIs and thread settlement logic.
  • Add DEFAULT_SIDEBAR_AUTO_SETTLE_COMPLETED_CHANGE_REQUESTS and sidebarAutoSettleCompletedChangeRequests to the ClientSettings schema and patch type with default-on decoding.
  • Update effectiveSettled to accept an autoSettleCompletedChangeRequests flag and only auto-settle on merged/closed change requests when this flag is true.
  • Extend related unit tests to cover the new option, manual-only workflows, and open-PR blocking semantics.
packages/contracts/src/settings.ts
packages/contracts/src/settings.test.ts
packages/client-runtime/src/state/threadSettled.ts
packages/client-runtime/src/state/threadSettled.test.ts
Wire the completed-PR auto-settle preference into web sidebar behavior, settings UI, search, restore/reset, persistence, and desktop settings.
  • Read sidebarAutoSettleCompletedChangeRequests from client settings and pass it through to effectiveSettled in Sidebar, ChatViewContent, and thread action menu logic.
  • Add completedPullRequestAutoSettleRestorePatch and integrate it into useSettingsRestore so resetting only affects the visible flat sidebar setting.
  • Expose an “Auto-settle completed pull requests” row in GeneralSettingsPanel, with searchability, reset behavior, and conditional visibility based on legacySidebarEnabled.
  • Filter settings-search items via getSettingsSearchItems and shouldShowCompletedPullRequestAutoSettleSetting so the toggle is hidden for the legacy sidebar.
  • Ensure browser and desktop client settings decode default the new flag to true and persist it correctly, with tests for localStorage and desktop JSON settings files.
apps/web/src/components/Sidebar.tsx
apps/web/src/components/ChatView.tsx
apps/web/src/hooks/useThreadActionMenu.ts
apps/web/src/components/settings/SettingsPanels.tsx
apps/web/src/components/settings/SettingsPanels.logic.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/components/settings/settingsSearch.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsSidebarNav.tsx
apps/web/src/clientPersistenceStorage.test.ts
apps/web/src/localApi.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
Add a device-local completed-PR auto-settle preference on mobile and integrate it with the flat thread list, settings UI, preferences hydration, and persistence.
  • Introduce autoSettleCompletedChangeRequests in mobile Preferences, sanitize it during load, and add tests to ensure valid values are preserved and malformed ones dropped.
  • Add resolveAutoSettleCompletedChangeRequests in the thread list logic to default to the shared contract’s value once preferences are loaded, but keep threads visible while hydration is in progress.
  • Pass autoSettleCompletedChangeRequests into buildThreadListV2Items and effectiveSettled so completed-PR settlement on mobile respects the toggle while inactivity settlement remains independent.
  • Provide a useAutoSettleCompletedChangeRequests hook that reads the hydrated mobile preferences and feeds the resolved value into HomeScreen and ThreadNavigationSidebar.
  • Gate the General settings switch row via shouldShowAutoSettleCompletedChangeRequestsSetting so the control appears only when preferences are loaded and the v2 flat list is enabled.
apps/mobile/src/persistence/mobile-preferences.ts
apps/mobile/src/persistence/mobile-preferences.test.ts
apps/mobile/src/features/threads/threadListV2.ts
apps/mobile/src/features/threads/threadListV2.test.ts
apps/mobile/src/features/threads/use-auto-settle-completed-change-requests.ts
apps/mobile/src/features/home/HomeScreen.tsx
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
apps/mobile/src/features/settings/SettingsRouteScreen.tsx
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.ts
apps/mobile/src/features/settings/autoSettleCompletedChangeRequests.test.ts
Update user documentation and tests to reflect the new completed-PR auto-settle preference and its interaction with inactivity-based settlement across platforms.
  • Document separate auto-settle controls for inactive and completed-PR threads, platform-specific behavior, and how to disable both for fully manual workflows.
  • Add or extend tests covering mobile thread list behavior under the new toggle, including default-on behavior for existing installs, hydration safety, and independence from inactivity settlement.
  • Verify settings search, restore labeling, and desktop/web persistence defaults for the new preference via targeted unit tests.
docs/user/thread-sidebar.md
apps/mobile/src/features/threads/threadListV2.test.ts
apps/web/src/components/settings/settingsSearch.test.ts
apps/web/src/components/settings/SettingsPanels.logic.test.ts
apps/web/src/clientPersistenceStorage.test.ts
apps/desktop/src/settings/DesktopClientSettings.test.ts
apps/mobile/src/persistence/mobile-preferences.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Defmon3
Defmon3 marked this pull request as ready for review August 13, 2026 17:58
@Defmon3
Defmon3 merged commit 65a138f into customAug 13, 2026
5 of 7 checks passed

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The web helper shouldShowCompletedPullRequestAutoSettleSetting and the mobile helper shouldShowAutoSettleCompletedChangeRequestsSetting implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
  • On mobile, both SettingsRouteScreen.GeneralSettingsSection and useAutoSettleCompletedChangeRequests derive preferencesLoaded independently from mobilePreferencesAtom; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The web helper `shouldShowCompletedPullRequestAutoSettleSetting` and the mobile helper `shouldShowAutoSettleCompletedChangeRequestsSetting` implement similar gating logic but with slightly different criteria and naming; consider standardizing the naming and semantics (or documenting the differences) to make it clearer why the behaviors diverge.
- On mobile, both `SettingsRouteScreen.GeneralSettingsSection` and `useAutoSettleCompletedChangeRequests` derive `preferencesLoaded` independently from `mobilePreferencesAtom`; you could centralize this into the hook (returning both value and loaded state) to avoid duplicated logic and keep the hydration behavior consistent.
## Individual Comments### Comment 1
<locationpath="apps/web/src/components/settings/SettingsPanels.tsx"line_range="1867-1872" />
<code_context>
 <SettingsRow {...searchableSetting("auto-settle-inactive-threads")}- description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
</code_context>
<issue_to_address>
**suggestion:** The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.
The new wording for `auto-settle-inactive-threads` (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.
Consider rephrasing both descriptions to clarify that:
- inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
- completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.
This should make the interaction between the two settings clearer to users.
Suggested implementation:
```typescript<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (
```You’ll also want to update the description text for the completed pull request auto-settle setting (likely another `SettingsRow` using something like `searchableSetting("auto-settle-completed-pull-requests")` or similar) to explain its relationship with inactivity-based settling. For example:- Old (hypothetical) description:`description="Sidebar threads settle automatically when their pull request is merged or closed."`- New description:`description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."`Please adjust the exact `SettingsRow` and `searchableSetting` key names to match the actual code where the completed-PR toggle is defined.</issue_to_address>### Comment 2<location path="apps/web/src/components/settings/settingsSearch.ts" line_range="211" /><code_context> export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];+export function shouldShowCompletedPullRequestAutoSettleSetting(input: {+ readonly legacySidebarEnabled: boolean;+}): boolean {</code_context><issue_to_address>**issue (complexity):** Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.### 1. Inline the trivial boolean and remove `shouldShowCompletedPullRequestAutoSettleSetting`The helper is just a negation; it doesn’t buy much. You can make `getSettingsSearchItems` the single place that encodes the condition:```ts
export function getSettingsSearchItems(input: {
readonly legacySidebarEnabled: boolean;
}): ReadonlyArray<SettingsSearchItem> {
if (input.legacySidebarEnabled) {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>item.id!=="auto-settle-completed-pull-requests",
);
}
return SETTINGS_SEARCH_ITEMS;
}
```Then at call sites that need to know visibility of the specific setting, use the inline boolean:```tsconstshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;
```This keeps behavior but removes a single-use wrapper function.### 2. Keep `SETTINGS_SEARCH_ITEMS` as the single source of truthRight now `getSettingsSearchItems` is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make `getSettingsSearchItems` generic:```tsconstSETTINGS_SEARCH_ITEMS= [
{
id: "auto-settle-completed-pull-requests",
title: "Auto-settle completed pull requests",
to: "/settings/general",
hiddenWhenLegacySidebar: true,
},
//
] asconstsatisfiesReadonlyArray<SettingsSearchItem& {
hiddenWhenLegacySidebar?:boolean;
}>;
exportfunction getSettingsSearchItems(input: {
readonly legacySidebarEnabled:boolean;
}):ReadonlyArray<SettingsSearchItem> {
returnSETTINGS_SEARCH_ITEMS.filter(
(item) =>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,
);
}
```This way:- `SETTINGS_SEARCH_ITEMS` remains the canonical list.- The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.- The helper scales if you later add more legacy-dependent items.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 1867 to 1872
<SettingsRow
{...searchableSetting("auto-settle-inactive-threads")}
description="Sidebar threads with no activity for this long settle automatically. Threads on merged or closed PRs always settle."
description="Sidebar threads with no activity for this long settle automatically. Independent of merged or closed pull requests."
resetAction={
settings.sidebarAutoSettleAfterDays !==
DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The auto-settle descriptions for inactivity and completed pull requests may conflict or confuse users.

The new wording for auto-settle-inactive-threads (“Independent of merged or closed pull requests.”) seems inconsistent with the completed-PR toggle, which explicitly settles threads on merge/close. This could suggest that inactivity never interacts with PR state, even though open PRs still block inactivity settlement and closed/merged PRs can now settle via the new toggle.

Consider rephrasing both descriptions to clarify that:

  • inactivity-based auto-settle depends on the time window and is blocked while the PR is open; and
  • completed-PR auto-settle is a separate control that can settle threads immediately on merge/close, or otherwise leave them to follow the normal inactivity rules.

This should make the interaction between the two settings clearer to users.

Suggested implementation:

<SettingsRow{...searchableSetting("auto-settle-inactive-threads")}description="Sidebar threads with no activity for this long settle automatically, as long as their pull request is not open or settled immediately by the completed pull request setting."resetAction={
settings.sidebarAutoSettleAfterDays!==DEFAULT_UNIFIED_SETTINGS.sidebarAutoSettleAfterDays ? (

You’ll also want to update the description text for the completed pull request auto-settle setting (likely another SettingsRow using something like searchableSetting("auto-settle-completed-pull-requests") or similar) to explain its relationship with inactivity-based settling. For example:

  • Old (hypothetical) description:
    description="Sidebar threads settle automatically when their pull request is merged or closed."

  • New description:
    description="Sidebar threads on completed pull requests settle immediately when the pull request is merged or closed. When this is off, threads on completed pull requests follow the normal inactivity-based auto-settle rules."

Please adjust the exact SettingsRow and searchableSetting key names to match the actual code where the completed-PR toggle is defined.


export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"];

export function shouldShowCompletedPullRequestAutoSettleSetting(input: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new settings helpers by inlining the legacy sidebar check and/or encoding visibility directly in SETTINGS_SEARCH_ITEMS instead of using a separate single-purpose wrapper function.

You can simplify the new helpers while keeping the new behavior (the extra setting + legacy sidebar hiding) and avoiding extra indirection.

1. Inline the trivial boolean and remove shouldShowCompletedPullRequestAutoSettleSetting

The helper is just a negation; it doesn’t buy much. You can make getSettingsSearchItems the single place that encodes the condition:

exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{if(input.legacySidebarEnabled){returnSETTINGS_SEARCH_ITEMS.filter((item)=>item.id!=="auto-settle-completed-pull-requests",);}returnSETTINGS_SEARCH_ITEMS;}

Then at call sites that need to know visibility of the specific setting, use the inline boolean:

constshowCompletedPullRequestAutoSettleSetting=!legacySidebarEnabled;

This keeps behavior but removes a single-use wrapper function.

2. Keep SETTINGS_SEARCH_ITEMS as the single source of truth

Right now getSettingsSearchItems is effectively a second “list” definition via filtering logic. If you prefer a more data-driven approach, you can encode the visibility on the item itself and make getSettingsSearchItems generic:

constSETTINGS_SEARCH_ITEMS=[{id: "auto-settle-completed-pull-requests",title: "Auto-settle completed pull requests",to: "/settings/general",hiddenWhenLegacySidebar: true,},// …]asconstsatisfiesReadonlyArray<SettingsSearchItem&{hiddenWhenLegacySidebar?: boolean;}>;exportfunctiongetSettingsSearchItems(input: {readonlylegacySidebarEnabled: boolean;}): ReadonlyArray<SettingsSearchItem>{returnSETTINGS_SEARCH_ITEMS.filter((item)=>!input.legacySidebarEnabled||!item.hiddenWhenLegacySidebar,);}

This way:

  • SETTINGS_SEARCH_ITEMS remains the canonical list.
  • The legacy-specific visibility is encoded in the data, not scattered in helper-specific conditions.
  • The helper scales if you later add more legacy-dependent items.

@Defmon3Defmon3 changed the title feat(settings): control completed pull request auto-settlementfix(settings): respect completed pull request auto-settlement preferenceAug 13, 2026
Defmon3 added a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:Lvouch:trustedPR 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

@Defmon3