From 5822f367ebfad3a845b25073abebe3292223b893 Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 09:26:54 -0700 Subject: [PATCH 1/7] refactor(runtime): owner transports, the creation default and a focus-read ratchet Adds runtimeTargetForOwnerEnvironment, runtimeTargetForOwnerHostId and runtimeTargetForWorkspaceOwner so routed call sites can get a transport from the resource's owner, and defaultCreationHost as the one sanctioned reader of the Active Server setting for creation flows with no source row. The owner-routing ratchet gains a second per-file counter for reads of the setting (member reads, the focus-routing helpers and defaultCreationHost), so swapping a helper for a direct read can no longer lower a count. Baseline rows may carry a trailing note that survives --prune. --- config/focus-setting-read-baseline.txt | 226 ++++++++++++++++++ .../scripts/check-owner-routing-ratchet.mjs | 157 ++++++++---- .../check-owner-routing-ratchet.test.mjs | 39 ++- .../src/lib/default-creation-host.test.ts | 13 + src/renderer/src/lib/default-creation-host.ts | 13 + src/renderer/src/lib/resolve-owner.test.ts | 31 +++ src/renderer/src/lib/resolve-owner.ts | 10 + .../src/runtime/runtime-client-target.test.ts | 29 ++- .../src/runtime/runtime-client-target.ts | 25 ++ 9 files changed, 496 insertions(+), 47 deletions(-) create mode 100644 config/focus-setting-read-baseline.txt create mode 100644 src/renderer/src/lib/default-creation-host.test.ts create mode 100644 src/renderer/src/lib/default-creation-host.ts diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt new file mode 100644 index 000000000000..7b7cc1fb6909 --- /dev/null +++ b/config/focus-setting-read-baseline.txt @@ -0,0 +1,226 @@ +# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the +# focus-routing helpers and defaultCreationHost(, per file. +# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read +# the default host, through defaultCreationHost. Everything else routes by the owner. +# Prune after removing reads: pnpm check:owner-routing-ratchet --prune +1 src/renderer/src/components/LinearIssueWorkspace.tsx +2 src/renderer/src/components/agent/AgentSettingsDialog.tsx +1 src/renderer/src/components/automations/automation-host-client.ts +2 src/renderer/src/components/automations/use-automations-page-setup-state.ts +1 src/renderer/src/components/cmd-j/quick-action-context.ts +2 src/renderer/src/components/editor/EditorPanel.tsx +1 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx +1 src/renderer/src/components/editor/ExternalFileChangeCompareDialog.tsx +1 src/renderer/src/components/editor/combined-diff/load-sections/fetch-combined-diff-section.ts +2 src/renderer/src/components/editor/csv/CsvPagedViewer.tsx +4 src/renderer/src/components/editor/editor-dropped-file-open.ts +3 src/renderer/src/components/editor/editor-restored-tab-conflict-scan.ts +1 src/renderer/src/components/editor/markdown-document-list-request.ts +1 src/renderer/src/components/editor/markdown-preview-link-actions.ts +1 src/renderer/src/components/editor/restored-editor-workspace-runtime-owner.ts +2 src/renderer/src/components/editor/rich-markdown-image-context.ts +2 src/renderer/src/components/editor/rich-markdown-image-insert.ts +2 src/renderer/src/components/editor/rich-markdown-paste-image.ts +1 src/renderer/src/components/editor/use-markdown-preview-source-foundation.ts +1 src/renderer/src/components/editor/useEditorPanelDiffContentLoader.ts +3 src/renderer/src/components/editor/useEditorPanelFileContentLoader.ts +1 src/renderer/src/components/editor/useLocalImageSrc.ts +3 src/renderer/src/components/editor/useMarkdownDocuments.ts +1 src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx +1 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-panel.tsx +2 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-request-actions.ts +1 src/renderer/src/components/github-project/ProjectCellIssueType.tsx +4 src/renderer/src/components/github-project/project-picker-runtime.ts +1 src/renderer/src/components/github-project/slug-dialog/Comments.tsx +1 src/renderer/src/components/github-project/slug-dialog/SlugDialogBody.tsx +1 src/renderer/src/components/github/github-rate-limit-display.tsx +4 src/renderer/src/components/github/github-work-item-edit-mutations.ts +1 src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx +1 src/renderer/src/components/landing-preflight-runtime.ts +1 src/renderer/src/components/linear-api-key-dialog.tsx +2 src/renderer/src/components/native-chat/native-chat-file-link-existence.ts +1 src/renderer/src/components/new-workspace/use-linear-source-connection.ts +1 src/renderer/src/components/ports/WorkspacePortScanner.tsx +1 src/renderer/src/components/pull-request-page/actions/panel.tsx +1 src/renderer/src/components/pull-request-page/reviewers/panel.tsx +2 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts +1 src/renderer/src/components/quick-open-file-list.ts +2 src/renderer/src/components/quick-open-file-navigation.ts +2 src/renderer/src/components/right-sidebar/file-explorer-deletion-editors.ts +1 src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +1 src/renderer/src/components/right-sidebar/push-target-upstream-refresh-cache.ts +2 src/renderer/src/components/right-sidebar/source-control/commit/use-entry-mutations.ts +1 src/renderer/src/components/right-sidebar/source-control/listing/use-submodule-status.ts +1 src/renderer/src/components/right-sidebar/source-control/listing/use-worktree-context.ts +1 src/renderer/src/components/right-sidebar/source-control/sync/use-git-history.ts +1 src/renderer/src/components/right-sidebar/source-control/sync/use-status-refresh.ts +1 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts +1 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts +1 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts +1 src/renderer/src/components/settings/CliSkillRuntimeSetup.tsx +1 src/renderer/src/components/settings/GeneralPane.tsx +2 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx +1 src/renderer/src/components/settings/RepositoryIconPicker.tsx +3 src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx +1 src/renderer/src/components/settings/linear-agent-skill-install-cta.tsx +2 src/renderer/src/components/settings/provider-account-scope.ts +1 src/renderer/src/components/settings/use-runtime-environment-mutation-actions.ts +1 src/renderer/src/components/settings/use-settings-store-model.ts +1 src/renderer/src/components/setup-guide/setup-guide-progress-readiness.ts +2 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +1 src/renderer/src/components/sidebar/add-repo-runtime-owner.ts +1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts +2 src/renderer/src/components/sidebar/sidebar-host-options.ts +1 src/renderer/src/components/sidebar/sidebar-project-drop.ts +1 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx +1 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts +5 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx +1 src/renderer/src/components/status-bar/SshStatusSegment.tsx +2 src/renderer/src/components/status-bar/provider-account-sync-key.ts +1 src/renderer/src/components/status-bar/status-bar-claude-accounts.ts +1 src/renderer/src/components/status-bar/status-bar-codex-accounts.ts +1 src/renderer/src/components/status-bar/usage-provider-settings-target.ts +5 src/renderer/src/components/status-bar/use-codex-switcher-controller.ts +3 src/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-test-harness.ts +1 src/renderer/src/components/tab-bar/tab-create-entry-local-path.ts +1 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts +1 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts +1 src/renderer/src/components/tab-bar/windows-shell-menu-visibility.ts +2 src/renderer/src/components/task-page-github-reviewer-actions.ts +1 src/renderer/src/components/task-page/github/AssigneesCell.tsx +2 src/renderer/src/components/task-page/github/MergeCell.tsx +1 src/renderer/src/components/task-page/github/StatusCell.tsx +2 src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts +1 src/renderer/src/components/terminal-pane/terminal-file-path-mapping.ts +1 src/renderer/src/components/terminal-pane/terminal-fit-restore.ts +1 src/renderer/src/components/terminal-pane/terminal-host-workspace-file.ts +1 src/renderer/src/components/terminal-pane/terminal-link-open-hints.ts +2 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts +1 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts +1 src/renderer/src/components/use-task-page-github-issue-draft.ts +2 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts +3 src/renderer/src/hooks/composer-state/attachment-drop-state.ts +1 src/renderer/src/hooks/composer-state/composer-navigation-actions.ts +1 src/renderer/src/hooks/composer-state/gitlab-provider-selection.ts +1 src/renderer/src/hooks/composer-state/provider-runtime-sync.ts +1 src/renderer/src/hooks/composer-state/quick-creation-execution.ts +1 src/renderer/src/hooks/composer-state/workspace-identity-state.ts +1 src/renderer/src/hooks/editor-external-watch-targets.ts +1 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts +1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts +1 src/renderer/src/hooks/ipc-events/settings-sidebar-ipc-bridge.ts +1 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts +2 src/renderer/src/hooks/useGitHubRepoMetadata.ts +4 src/renderer/src/hooks/useGitHubSlugMetadata.ts +1 src/renderer/src/hooks/useIssueMetadata.ts +2 src/renderer/src/hooks/useSettingsNavigationMetadata.ts +1 src/renderer/src/hooks/useWindowsTerminalCapabilityOwnerKey.ts +3 src/renderer/src/lib/activate-ai-vault-structured-session.ts +1 src/renderer/src/lib/active-agent-note-send.ts +1 src/renderer/src/lib/active-agent-note-target.ts +1 src/renderer/src/lib/agent-hibernation-coordinator-test-fixture.ts +1 src/renderer/src/lib/automation-session-observer.ts +1 src/renderer/src/lib/client-creation-action-policy.ts +1 src/renderer/src/lib/codex-pane-selection-lane.ts +1 src/renderer/src/lib/file-search-result-owner.ts +1 src/renderer/src/lib/github-pr-start-point.ts +1 src/renderer/src/lib/github-source-runtime-context.ts +1 src/renderer/src/lib/hosted-review-repo-read.ts +3 src/renderer/src/lib/http-link-routing.ts +1 src/renderer/src/lib/launch-agent-background-session.ts +1 src/renderer/src/lib/launch-worktree-background-terminals.ts +2 src/renderer/src/lib/local-file-access.ts +1 src/renderer/src/lib/local-preflight-context.ts +2 src/renderer/src/lib/provider-runtime-context.ts +1 src/renderer/src/lib/repo-runtime-owner.ts +1 src/renderer/src/lib/repo-slug-cache.ts +1 src/renderer/src/lib/repo-slug-index.ts +1 src/renderer/src/lib/resolve-owner.ts +1 src/renderer/src/lib/runtime-session-mirror-owners.ts +1 src/renderer/src/lib/single-runtime-legacy-owner.ts +1 src/renderer/src/lib/structured-agent-session-tab-activation.ts +2 src/renderer/src/lib/unread-badge-count-selector.ts +1 src/renderer/src/lib/workspace-file-host-routing.ts +1 src/renderer/src/lib/worktree-creation-flow-startup.ts +1 src/renderer/src/lib/worktree-operation-route.ts +1 src/renderer/src/runtime/mobile-markdown-bridge.ts +1 src/renderer/src/runtime/runtime-client-target.ts +1 src/renderer/src/runtime/runtime-file-import-client.ts +1 src/renderer/src/runtime/runtime-file-metadata-client.ts +1 src/renderer/src/runtime/runtime-file-mutation-client.ts +1 src/renderer/src/runtime/runtime-file-range-client.ts +2 src/renderer/src/runtime/runtime-file-read-client.ts +4 src/renderer/src/runtime/runtime-file-routing.ts +4 src/renderer/src/runtime/runtime-file-search-client.ts +1 src/renderer/src/runtime/runtime-file-watch-client.ts +1 src/renderer/src/runtime/runtime-git-client-context.ts +5 src/renderer/src/runtime/runtime-git-diff-client.ts +5 src/renderer/src/runtime/runtime-git-generation-client.ts +6 src/renderer/src/runtime/runtime-git-status-client.ts +10 src/renderer/src/runtime/runtime-git-sync-client.ts +9 src/renderer/src/runtime/runtime-git-working-tree-client.ts +3 src/renderer/src/runtime/runtime-hooks-client.ts +1 src/renderer/src/runtime/runtime-jira-target.ts +1 src/renderer/src/runtime/runtime-linear-client.ts +3 src/renderer/src/runtime/runtime-repo-client.ts +3 src/renderer/src/runtime/runtime-terminal-inspection.ts +1 src/renderer/src/runtime/runtime-terminal-stream.ts +1 src/renderer/src/runtime/runtime-terminal-verified-input.ts +2 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts +1 src/renderer/src/runtime/web-runtime-browser-creation.ts +1 src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts +1 src/renderer/src/runtime/web-runtime-session-tab-move.ts +1 src/renderer/src/runtime/web-runtime-terminal-create-operation.ts +1 src/renderer/src/runtime/web-runtime-worktree-activation.ts +1 src/renderer/src/runtime/web-session-tabs-sync/global-session-subscription.ts +1 src/renderer/src/runtime/web-session-tabs-sync/tracking-decisions.ts +1 src/renderer/src/store/folder-workspaces/folder-path-status-actions.ts +2 src/renderer/src/store/folder-workspaces/folder-workspace-catalog-actions.ts +3 src/renderer/src/store/folder-workspaces/folder-workspace-mutations.ts +1 src/renderer/src/store/folder-workspaces/folder-workspace-routing.ts +1 src/renderer/src/store/github/cache-identity.ts +1 src/renderer/src/store/github/project-actions.ts +1 src/renderer/src/store/github/project-field-mutations.ts +2 src/renderer/src/store/github/project-row-actions.ts +1 src/renderer/src/store/github/repository-routing.ts +1 src/renderer/src/store/github/work-item-mutation-actions.ts +6 src/renderer/src/store/project-groups/nested-repository-operations.ts +2 src/renderer/src/store/project-groups/project-group-catalog-actions.ts +4 src/renderer/src/store/project-groups/project-group-mutations.ts +1 src/renderer/src/store/projects/project-host-routing.ts +2 src/renderer/src/store/repos/all-host-repo-catalog-actions.ts +1 src/renderer/src/store/repos/owner-routing.ts +2 src/renderer/src/store/repos/repo-add-actions.ts +2 src/renderer/src/store/repos/repo-catalog-actions.ts +1 src/renderer/src/store/repos/repo-removal.ts +1 src/renderer/src/store/repos/repo-update.ts +2 src/renderer/src/store/repos/runtime-repo-catalog-actions.ts +1 src/renderer/src/store/slices/diff-comment-persistence.ts +1 src/renderer/src/store/slices/editor/actions/markdown-link-action.ts +1 src/renderer/src/store/slices/editor/actions/markdown-preview-actions.ts +1 src/renderer/src/store/slices/editor/actions/open-file-apply.ts +1 src/renderer/src/store/slices/github-cache-key.ts +4 src/renderer/src/store/slices/hosted-review.ts +1 src/renderer/src/store/slices/preflight.ts +1 src/renderer/src/store/slices/project-group-owner-routing.ts +1 src/renderer/src/store/slices/runtime-status.ts +1 src/renderer/src/store/slices/settings.ts +1 src/renderer/src/store/slices/worktree-visibility-owner-settings.ts +3 src/renderer/src/store/slices/worktree-visibility-settings-write.ts +1 src/renderer/src/store/slices/worktrees/create/create-worktree.ts +1 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts +2 src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts +1 src/renderer/src/store/slices/worktrees/listing/detected-worktree-refresh.ts +1 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts +1 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts +2 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts +1 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts +1 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts +3 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts +1 src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.ts +1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts +1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts +1 src/renderer/src/store/slices/worktrees/teardown/remove-worktree.ts +2 src/renderer/src/web/preload-api/web-preference-normalization.ts +5 src/renderer/src/web/preload-api/web-preferences-store.ts +1 src/renderer/src/web/preload-api/web-settings-api.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index c3304373fa5c..a3b12c1fe097 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -4,33 +4,78 @@ import path from 'node:path' import process from 'node:process' import { pathToFileURL } from 'node:url' -// Ratchet for renderer code that routes host-owned work by the "Active Server" focus setting -// instead of by the resource's own host. The three helpers are counted together so renaming one -// into another never lowers the count. Per-file counts may only go down. +// Two ratchets for renderer code that routes host-owned work by the "Active Server" setting +// instead of by the resource's own host. Per-file counts may only go down. +// - Owner routing: the three focus-routing helpers, counted together so renaming one into another +// never lowers the count. +// - Focus reads: every read of the setting, including the helpers and `defaultCreationHost`, so +// swapping a helper for a direct read or for the creation default never lowers it either. -const BASELINE_PATH = 'config/owner-routing-baseline.txt' const SCAN_ROOT = 'src/renderer/src' -const HELPERS = '(?:getActiveRuntimeTarget|legacyRouteFromSettings|settingsForRuntimeOwner)' +const HELPER_NAMES = 'getActiveRuntimeTarget|legacyRouteFromSettings|settingsForRuntimeOwner' +const HELPERS = `(?:${HELPER_NAMES})` const IMPORT_EXPORT_LIST = /\b(?:import|export)\s+(?:type\s+)?\{[^}]*\}/g // Calls and value uses (`.map(helper)`); definitions and type queries are not routing. const FOCUS_ROUTING_USE = new RegExp(`(? FOCUS_ROUTING_ALIAS.test(list)) } +export const RATCHETS = [ + { + name: 'owner-routing', + baselinePath: 'config/owner-routing-baseline.txt', + count: countFocusRoutingCalls, + header: [ + '# Renderer call sites that route by the Active Server focus setting:', + '# getActiveRuntimeTarget( + legacyRouteFromSettings( + settingsForRuntimeOwner(, per file.', + '# This is a RATCHET: counts may only go DOWN. Route new work by the resource owner instead.', + '# Prune after removing sites: pnpm check:owner-routing-ratchet --prune' + ], + advice: + "Route by the resource's owner (resolveOwner + callHostRoute) instead of the Active Server setting." + }, + { + name: 'focus-setting-read', + baselinePath: 'config/focus-setting-read-baseline.txt', + count: countFocusSettingReads, + header: [ + '# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the', + '# focus-routing helpers and defaultCreationHost(, per file.', + '# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read', + '# the default host, through defaultCreationHost. Everything else routes by the owner.', + '# Prune after removing reads: pnpm check:owner-routing-ratchet --prune' + ], + advice: + "Route by the resource's owner. Only a creation flow with no source row may use defaultCreationHost." + } +] + export function isScannedPath(rel) { return /\.(ts|tsx)$/.test(rel) && !/\.(test|spec)\.tsx?$/.test(rel) } -/** ` ` lines; `#` comments and blanks ignored. */ +/** ` [# note]` lines; `#` comment lines and blanks ignored. */ export function parseBaseline(text) { const counts = new Map() for (const raw of text.split('\n')) { @@ -44,19 +89,26 @@ export function parseBaseline(text) { return counts } -export function formatBaseline(counts) { - const header = [ - '# Renderer call sites that route by the Active Server focus setting:', - '# getActiveRuntimeTarget( + legacyRouteFromSettings( + settingsForRuntimeOwner(, per file.', - '# This is a RATCHET: counts may only go DOWN. Route new work by the resource owner instead.', - '# Prune after removing sites: pnpm check:owner-routing-ratchet --prune', - '' - ].join('\n') +/** Trailing `# note` per row, so a pruned baseline keeps why an entry is still allowed. */ +export function parseBaselineNotes(text) { + const notes = new Map() + for (const raw of text.split('\n')) { + const match = /^\s*\d+\s+(\S+)\s+#\s*(.+?)\s*$/.exec(raw) + if (match) { + notes.set(match[1], match[2]) + } + } + return notes +} + +export function formatBaseline(counts, header = RATCHETS[0].header, notes = new Map()) { const rows = [...counts] .filter(([, count]) => count > 0) .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) - .map(([file, count]) => `${count} ${file}`) - return `${header}${rows.join('\n')}\n` + .map(([file, count]) => + notes.has(file) ? `${count} ${file} # ${notes.get(file)}` : `${count} ${file}` + ) + return `${[...header, ''].join('\n')}${rows.join('\n')}${rows.length > 0 ? '\n' : ''}` } /** `grown`: above baseline (fails). `shrunk`: below baseline, must be pruned so it cannot regrow. */ @@ -76,7 +128,7 @@ export function diffCounts(current, baseline) { return { grown: grown.sort(byFile), shrunk: shrunk.sort(byFile) } } -export function collectCurrentCounts(root = process.cwd()) { +export function collectCurrentCounts(root = process.cwd(), count = countFocusRoutingCalls) { const tracked = execFileSync('git', ['ls-files', SCAN_ROOT], { cwd: root, encoding: 'utf8', @@ -93,9 +145,9 @@ export function collectCurrentCounts(root = process.cwd()) { } catch { continue } - const count = countFocusRoutingCalls(source) - if (count > 0) { - counts.set(rel, count) + const found = count(source) + if (found > 0) { + counts.set(rel, found) } if (hasFocusRoutingAlias(source)) { aliased.push(rel) @@ -108,50 +160,65 @@ function total(counts) { return [...counts.values()].reduce((sum, count) => sum + count, 0) } -export function main(root = process.cwd()) { - const baselineFile = path.join(root, BASELINE_PATH) +function checkRatchet(root, ratchet) { + const baselineFile = path.join(root, ratchet.baselinePath) if (!fs.existsSync(baselineFile)) { - console.error(`::error::Missing ${BASELINE_PATH}.`) - return 1 + console.error(`::error::Missing ${ratchet.baselinePath}.`) + return { ok: false, total: 0 } } const baseline = parseBaseline(fs.readFileSync(baselineFile, 'utf8')) - const { counts: current, aliased } = collectCurrentCounts(root) + const { counts: current } = collectCurrentCounts(root, ratchet.count) const { grown, shrunk } = diffCounts(current, baseline) - for (const file of aliased) { + for (const { file, now, allowed } of grown) { console.error( - `::error file=${file}::A focus-routing helper is imported or exported under another name, which hides its calls from this ratchet. Use the original name.` + `::error file=${file}::${ratchet.name}: ${now} use(s), baseline allows ${allowed}. ${ratchet.advice}` ) } - for (const { file, now, allowed } of grown) { + for (const { file, now, allowed } of shrunk) { console.error( - `::error file=${file}::${now} focus-routed call(s), baseline allows ${allowed}. Route by the resource's owner (resolveOwner + callHostRoute) instead of the Active Server setting.` + `::error file=${file}::${ratchet.name}: ${now} use(s), baseline still allows ${allowed}. Run: pnpm check:owner-routing-ratchet --prune` ) } - for (const { file, now, allowed } of shrunk) { + return { ok: grown.length === 0 && shrunk.length === 0, total: total(current) } +} + +export function main(root = process.cwd()) { + const { aliased } = collectCurrentCounts(root) + for (const file of aliased) { console.error( - `::error file=${file}::${now} focus-routed call(s), baseline still allows ${allowed}. Run: pnpm check:owner-routing-ratchet --prune` + `::error file=${file}::A focus-routing helper is imported or exported under another name, which hides its calls from this ratchet. Use the original name.` ) } - if (aliased.length > 0 || grown.length > 0 || shrunk.length > 0) { - return 1 + let ok = aliased.length === 0 + for (const ratchet of RATCHETS) { + const result = checkRatchet(root, ratchet) + ok = ok && result.ok + if (result.ok) { + console.log(`${ratchet.name} ratchet OK — ${result.total} use(s).`) + } } - console.log(`Owner-routing ratchet OK — ${total(current)} focus-routed call site(s).`) - return 0 + return ok ? 0 : 1 } -if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - const root = process.cwd() - if (process.argv[2] === '--prune') { - const baselineFile = path.join(root, BASELINE_PATH) - const baseline = parseBaseline(fs.readFileSync(baselineFile, 'utf8')) - const { counts: current } = collectCurrentCounts(root) +export function prune(root = process.cwd()) { + for (const ratchet of RATCHETS) { + const baselineFile = path.join(root, ratchet.baselinePath) + const text = fs.existsSync(baselineFile) ? fs.readFileSync(baselineFile, 'utf8') : '' + const baseline = parseBaseline(text) + const { counts: current } = collectCurrentCounts(root, ratchet.count) // Lowers entries only; growth still has to be fixed in the code. const pruned = new Map( [...baseline].map(([file, allowed]) => [file, Math.min(allowed, current.get(file) ?? 0)]) ) - fs.writeFileSync(baselineFile, formatBaseline(pruned)) - console.log(`Pruned ${BASELINE_PATH} to ${total(pruned)} call site(s).`) - process.exit(main(root)) + fs.writeFileSync(baselineFile, formatBaseline(pruned, ratchet.header, parseBaselineNotes(text))) + console.log(`Pruned ${ratchet.baselinePath} to ${total(pruned)} use(s).`) + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const root = process.cwd() + if (process.argv[2] === '--prune') { + prune(root) } process.exit(main(root)) } diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index c33b9304121e..f2ec389504f2 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -2,11 +2,13 @@ import { describe, expect, it } from 'vitest' import { countFocusRoutingCalls, + countFocusSettingReads, diffCounts, formatBaseline, hasFocusRoutingAlias, isScannedPath, - parseBaseline + parseBaseline, + parseBaselineNotes } from './check-owner-routing-ratchet.mjs' describe('countFocusRoutingCalls', () => { @@ -39,6 +41,29 @@ describe('countFocusRoutingCalls', () => { }) }) +describe('countFocusSettingReads', () => { + it('counts setting reads, the helpers and the creation default together', () => { + const src = [ + "import { defaultCreationHost } from './default-creation-host'", + 'const a = settings?.activeRuntimeEnvironmentId', + 'const b = state.settings.activeRuntimeEnvironmentId', + 'const c = getActiveRuntimeTarget(settings)', + 'const d = defaultCreationHost(settings)', + 'export function defaultCreationHost(settings) {}' + ].join('\n') + expect(countFocusSettingReads(src)).toBe(4) + }) + + it('does not count writes, keys or type positions', () => { + const src = [ + 'const owner = { activeRuntimeEnvironmentId: id }', + "type T = GlobalSettings['activeRuntimeEnvironmentId']", + 'updateSettings({ activeRuntimeEnvironmentId: null })' + ].join('\n') + expect(countFocusSettingReads(src)).toBe(0) + }) +}) + describe('hasFocusRoutingAlias', () => { it('refuses an aliased import or re-export, which would hide its calls', () => { expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget as route } from './rpc'")).toBe( @@ -47,6 +72,7 @@ describe('hasFocusRoutingAlias', () => { expect( hasFocusRoutingAlias("export {\n settingsForRuntimeOwner as owner\n} from './target'") ).toBe(true) + expect(hasFocusRoutingAlias("import { defaultCreationHost as host } from './d'")).toBe(true) expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget } from './rpc'")).toBe(false) }) }) @@ -75,6 +101,17 @@ describe('baseline', () => { ) }) + it('keeps a row note through a prune', () => { + const text = formatBaseline( + new Map([['src/a.ts', 1]]), + ['# header'], + new Map([['src/a.ts', 'waits for V4b']]) + ) + expect(text).toBe('# header\n1 src/a.ts # waits for V4b\n') + expect(parseBaseline(text)).toEqual(new Map([['src/a.ts', 1]])) + expect(parseBaselineNotes(text)).toEqual(new Map([['src/a.ts', 'waits for V4b']])) + }) + it('fails growth, including a new file, and asks to prune shrinkage', () => { const { grown, shrunk } = diffCounts( new Map([ diff --git a/src/renderer/src/lib/default-creation-host.test.ts b/src/renderer/src/lib/default-creation-host.test.ts new file mode 100644 index 000000000000..05ee35739059 --- /dev/null +++ b/src/renderer/src/lib/default-creation-host.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest' +import { defaultCreationHost } from './default-creation-host' + +describe('defaultCreationHost', () => { + it('is this computer unless a server is chosen', () => { + expect(defaultCreationHost(null)).toEqual({ kind: 'local' }) + expect(defaultCreationHost({ activeRuntimeEnvironmentId: null })).toEqual({ kind: 'local' }) + expect(defaultCreationHost({ activeRuntimeEnvironmentId: ' env-a ' })).toEqual({ + kind: 'environment', + environmentId: 'env-a' + }) + }) +}) diff --git a/src/renderer/src/lib/default-creation-host.ts b/src/renderer/src/lib/default-creation-host.ts new file mode 100644 index 000000000000..9fe9e06cff88 --- /dev/null +++ b/src/renderer/src/lib/default-creation-host.ts @@ -0,0 +1,13 @@ +import type { GlobalSettings } from '../../../shared/global-settings-types' +import type { RuntimeClientTarget } from '@/runtime/runtime-client-target' + +/** + * The "Default host for new projects" setting. Only a creation flow with no source row reads it; + * everything else routes by the resource's owner. + */ +export function defaultCreationHost( + settings: Pick | null | undefined +): RuntimeClientTarget { + const environmentId = settings?.activeRuntimeEnvironmentId?.trim() + return environmentId ? { kind: 'environment', environmentId } : { kind: 'local' } +} diff --git a/src/renderer/src/lib/resolve-owner.test.ts b/src/renderer/src/lib/resolve-owner.test.ts index dab7faced3db..2c20288b1b42 100644 --- a/src/renderer/src/lib/resolve-owner.test.ts +++ b/src/renderer/src/lib/resolve-owner.test.ts @@ -7,6 +7,7 @@ import { resetLegacyFocusFallbackCountForTest, resolveOwner, resolveOwnerWithLegacyFocus, + runtimeTargetForWorkspaceOwner, type WorkspaceOwnerRef } from './resolve-owner' import type { @@ -241,3 +242,33 @@ describe('resolveOwner characterization', () => { }) }) }) + +describe('runtimeTargetForWorkspaceOwner', () => { + const settings = { activeRuntimeEnvironmentId: 'env-other' } + it('dials the owner, not the focused server', () => { + const rows: WorktreeOperationRouteState = { + settings, + repos: [{ id: 'repo-1', executionHostId: 'runtime:env-a' }], + worktreesByRepo: { 'repo-1': [worktree('runtime:env-a', 'env-a')] } + } + expect(runtimeTargetForWorkspaceOwner(rows, { workspaceId: WORKTREE_ID })).toEqual({ + kind: 'environment', + environmentId: 'env-a' + }) + }) + + it('rides this app for local and direct SSH owners', () => { + const sshRows: WorktreeOperationRouteState = { + settings, + repos: [{ id: 'repo-1', connectionId: 'box' }], + worktreesByRepo: { 'repo-1': [worktree('ssh:box')] } + } + expect(runtimeTargetForWorkspaceOwner(sshRows, { workspaceId: WORKTREE_ID })).toEqual({ + kind: 'local' + }) + }) + + it('has no transport when no row names the workspace', () => { + expect(runtimeTargetForWorkspaceOwner({ settings }, { workspaceId: WORKTREE_ID })).toBeNull() + }) +}) diff --git a/src/renderer/src/lib/resolve-owner.ts b/src/renderer/src/lib/resolve-owner.ts index 6462f79ac896..c2753f50980b 100644 --- a/src/renderer/src/lib/resolve-owner.ts +++ b/src/renderer/src/lib/resolve-owner.ts @@ -26,6 +26,7 @@ import { } from './worktree-operation-route' import { getNestedSshTargetIdForFolderWorkspace } from './folder-workspace-runtime-owner' import { parseWorkspaceKey } from '../../../shared/workspace-scope' +import { hostRouteForAuthority, type RuntimeClientTarget } from '@/runtime/runtime-client-target' /** A worktree id, a `folder:` workspace key, or the floating workspace; `hostId` when the row names one. */ export type WorkspaceOwnerRef = { workspaceId: string; hostId?: ExecutionHostId } @@ -105,6 +106,15 @@ export function resolveOwner( return resolveRoute({ ...state, settings: null }, ref) } +/** Transport to the workspace's owner, or `null` when the rows name none or disagree. */ +export function runtimeTargetForWorkspaceOwner( + state: WorktreeOperationRouteState, + ref: WorkspaceOwnerRef +): RuntimeClientTarget | null { + const match = resolveOwner(state, ref) + return match.kind === 'resolved' ? hostRouteForAuthority(match.owner).target : null +} + let legacyFocusFallbackCount = 0 /** How many answers focus has changed since start; a slice drops its fallback once this stays 0. */ diff --git a/src/renderer/src/runtime/runtime-client-target.test.ts b/src/renderer/src/runtime/runtime-client-target.test.ts index 7519bd898b3d..93101dbdeacc 100644 --- a/src/renderer/src/runtime/runtime-client-target.test.ts +++ b/src/renderer/src/runtime/runtime-client-target.test.ts @@ -1,6 +1,11 @@ import { describe, expect, it } from 'vitest' import { UNRESOLVED_OWNER_HOST_ID } from '../../../shared/execution-host' -import { hostRouteForAuthority, runtimeTargetForExecutionHostId } from './runtime-client-target' +import { + hostRouteForAuthority, + runtimeTargetForExecutionHostId, + runtimeTargetForOwnerEnvironment, + runtimeTargetForOwnerHostId +} from './runtime-client-target' describe('runtimeTargetForExecutionHostId', () => { it('keeps the answers for plain host ids', () => { @@ -35,3 +40,25 @@ describe('hostRouteForAuthority', () => { }) }) }) + +describe('owner transports', () => { + it('maps an owner environment id, with null as this app', () => { + expect(runtimeTargetForOwnerEnvironment(null)).toEqual({ kind: 'local' }) + expect(runtimeTargetForOwnerEnvironment(' ')).toEqual({ kind: 'local' }) + expect(runtimeTargetForOwnerEnvironment(' env-a ')).toEqual({ + kind: 'environment', + environmentId: 'env-a' + }) + }) + + it('routes local and direct SSH owners through this app and never dials the sentinel', () => { + expect(runtimeTargetForOwnerHostId('local')).toEqual({ kind: 'local' }) + expect(runtimeTargetForOwnerHostId('ssh:box')).toEqual({ kind: 'local' }) + expect(runtimeTargetForOwnerHostId('runtime:env%2Fa')).toEqual({ + kind: 'environment', + environmentId: 'env/a' + }) + expect(runtimeTargetForOwnerHostId(UNRESOLVED_OWNER_HOST_ID)).toBeNull() + expect(runtimeTargetForOwnerHostId(undefined)).toBeNull() + }) +}) diff --git a/src/renderer/src/runtime/runtime-client-target.ts b/src/renderer/src/runtime/runtime-client-target.ts index 47bc13c150cb..e17f811f10d0 100644 --- a/src/renderer/src/runtime/runtime-client-target.ts +++ b/src/renderer/src/runtime/runtime-client-target.ts @@ -1,4 +1,5 @@ import type { GlobalSettings } from '../../../shared/global-settings-types' +import { parseRoutableExecutionHostId, type ExecutionHostId } from '../../../shared/execution-host' import { parseHostAuthorityKey, type HostAuthority, @@ -14,6 +15,30 @@ export function getActiveRuntimeTarget( return environmentId ? { kind: 'environment', environmentId } : { kind: 'local' } } +/** Transport to an owner's runtime environment; `null` is this app (local or direct SSH). */ +export function runtimeTargetForOwnerEnvironment( + environmentId: string | null +): RuntimeClientTarget { + const ownerId = environmentId?.trim() + return ownerId ? { kind: 'environment', environmentId: ownerId } : { kind: 'local' } +} + +/** + * Transport to the host that owns a row: a server's own runtime, or this app for local and direct + * SSH (which ride this app's IPC with a `connectionId`). `null` when the id names no routable host. + */ +export function runtimeTargetForOwnerHostId( + hostId: ExecutionHostId | null | undefined +): RuntimeClientTarget | null { + const parsed = parseRoutableExecutionHostId(hostId) + if (!parsed) { + return null + } + return parsed.kind === 'runtime' + ? { kind: 'environment', environmentId: parsed.environmentId } + : { kind: 'local' } +} + /** * RPC target for a dispatchable host; direct SSH and the unresolved-owner sentinel have none. A * nested authority key routes to the server that owns the SSH target. From f643022c71f0bd40ffb1636ec721f8ae4a6dcbbc Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 09:30:48 -0700 Subject: [PATCH 2/7] chore(lint): pin defaultCreationHost's own read in the focus-read baseline --- config/focus-setting-read-baseline.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 7b7cc1fb6909..7c893bb3dc0e 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -122,6 +122,7 @@ 1 src/renderer/src/lib/automation-session-observer.ts 1 src/renderer/src/lib/client-creation-action-policy.ts 1 src/renderer/src/lib/codex-pane-selection-lane.ts +1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host 1 src/renderer/src/lib/file-search-result-owner.ts 1 src/renderer/src/lib/github-pr-start-point.ts 1 src/renderer/src/lib/github-source-runtime-context.ts From f0a6eb176453a3a8f2d0f2ba7ad7f3ec612680c0 Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 09:34:32 -0700 Subject: [PATCH 3/7] fix(lint): the focus-read ratchet counts shared focus helpers and destructuring reads --- config/focus-setting-read-baseline.txt | 45 ++++++++++++++----- .../scripts/check-owner-routing-ratchet.mjs | 43 +++++++++++++----- .../check-owner-routing-ratchet.test.mjs | 35 ++++++++++++--- 3 files changed, 96 insertions(+), 27 deletions(-) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 7c893bb3dc0e..1bfe6042f14b 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -1,12 +1,15 @@ -# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the -# focus-routing helpers and defaultCreationHost(, per file. +# Renderer reads of the Active Server setting (member, element and destructuring reads of +# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file. # This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read # the default host, through defaultCreationHost. Everything else routes by the owner. # Prune after removing reads: pnpm check:owner-routing-ratchet --prune 1 src/renderer/src/components/LinearIssueWorkspace.tsx +2 src/renderer/src/components/activity/activity-thread-actions.ts +1 src/renderer/src/components/activity/use-agent-pane-threads.ts 2 src/renderer/src/components/agent/AgentSettingsDialog.tsx 1 src/renderer/src/components/automations/automation-host-client.ts 2 src/renderer/src/components/automations/use-automations-page-setup-state.ts +1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-stream-activation.ts 1 src/renderer/src/components/cmd-j/quick-action-context.ts 2 src/renderer/src/components/editor/EditorPanel.tsx 1 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx @@ -57,6 +60,7 @@ 1 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts 1 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts 1 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts +1 src/renderer/src/components/settings/BrowserPane.tsx 1 src/renderer/src/components/settings/CliSkillRuntimeSetup.tsx 1 src/renderer/src/components/settings/GeneralPane.tsx 2 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx @@ -68,12 +72,20 @@ 1 src/renderer/src/components/settings/use-settings-store-model.ts 1 src/renderer/src/components/setup-guide/setup-guide-progress-readiness.ts 2 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +1 src/renderer/src/components/sidebar/WorktreeList.tsx 1 src/renderer/src/components/sidebar/add-repo-runtime-owner.ts 1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts +1 src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts 2 src/renderer/src/components/sidebar/sidebar-host-options.ts 1 src/renderer/src/components/sidebar/sidebar-project-drop.ts +1 src/renderer/src/components/sidebar/use-add-repo-host-selection.ts +1 src/renderer/src/components/sidebar/use-visible-workspace-kanban-worktree-ids.ts +1 src/renderer/src/components/sidebar/visible-worktree-options-from-state.ts +2 src/renderer/src/components/sidebar/visible-worktrees.ts 1 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx -1 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts +1 src/renderer/src/components/sidebar/worktree-filter-visibility.ts +2 src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts +2 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts 5 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx 1 src/renderer/src/components/status-bar/SshStatusSegment.tsx 2 src/renderer/src/components/status-bar/provider-account-sync-key.ts @@ -98,6 +110,8 @@ 2 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts 1 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts 1 src/renderer/src/components/use-task-page-github-issue-draft.ts +1 src/renderer/src/components/use-task-page-source-availability.ts +1 src/renderer/src/components/use-worktree-jump-palette-filter.ts 2 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts 3 src/renderer/src/hooks/composer-state/attachment-drop-state.ts 1 src/renderer/src/hooks/composer-state/composer-navigation-actions.ts @@ -109,7 +123,7 @@ 1 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts 1 src/renderer/src/hooks/ipc-events/settings-sidebar-ipc-bridge.ts -1 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts +3 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts 2 src/renderer/src/hooks/useGitHubRepoMetadata.ts 4 src/renderer/src/hooks/useGitHubSlugMetadata.ts 1 src/renderer/src/hooks/useIssueMetadata.ts @@ -124,6 +138,7 @@ 1 src/renderer/src/lib/codex-pane-selection-lane.ts 1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host 1 src/renderer/src/lib/file-search-result-owner.ts +2 src/renderer/src/lib/folder-workspace-runtime-owner.ts 1 src/renderer/src/lib/github-pr-start-point.ts 1 src/renderer/src/lib/github-source-runtime-context.ts 1 src/renderer/src/lib/hosted-review-repo-read.ts @@ -133,17 +148,19 @@ 2 src/renderer/src/lib/local-file-access.ts 1 src/renderer/src/lib/local-preflight-context.ts 2 src/renderer/src/lib/provider-runtime-context.ts -1 src/renderer/src/lib/repo-runtime-owner.ts +2 src/renderer/src/lib/repo-runtime-owner.ts 1 src/renderer/src/lib/repo-slug-cache.ts 1 src/renderer/src/lib/repo-slug-index.ts 1 src/renderer/src/lib/resolve-owner.ts 1 src/renderer/src/lib/runtime-session-mirror-owners.ts 1 src/renderer/src/lib/single-runtime-legacy-owner.ts 1 src/renderer/src/lib/structured-agent-session-tab-activation.ts -2 src/renderer/src/lib/unread-badge-count-selector.ts +2 src/renderer/src/lib/terminal-worktree-route.ts +3 src/renderer/src/lib/unread-badge-count-selector.ts 1 src/renderer/src/lib/workspace-file-host-routing.ts 1 src/renderer/src/lib/worktree-creation-flow-startup.ts 1 src/renderer/src/lib/worktree-operation-route.ts +1 src/renderer/src/lib/worktree-runtime-owner.ts 1 src/renderer/src/runtime/mobile-markdown-bridge.ts 1 src/renderer/src/runtime/runtime-client-target.ts 1 src/renderer/src/runtime/runtime-file-import-client.ts @@ -167,7 +184,7 @@ 3 src/renderer/src/runtime/runtime-terminal-inspection.ts 1 src/renderer/src/runtime/runtime-terminal-stream.ts 1 src/renderer/src/runtime/runtime-terminal-verified-input.ts -2 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts +3 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts 1 src/renderer/src/runtime/web-runtime-browser-creation.ts 1 src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts 1 src/renderer/src/runtime/web-runtime-session-tab-move.ts @@ -183,7 +200,8 @@ 1 src/renderer/src/store/github/project-actions.ts 1 src/renderer/src/store/github/project-field-mutations.ts 2 src/renderer/src/store/github/project-row-actions.ts -1 src/renderer/src/store/github/repository-routing.ts +2 src/renderer/src/store/github/repository-routing.ts +1 src/renderer/src/store/github/visible-hosted-review-refresh-targets.ts 1 src/renderer/src/store/github/work-item-mutation-actions.ts 6 src/renderer/src/store/project-groups/nested-repository-operations.ts 2 src/renderer/src/store/project-groups/project-group-catalog-actions.ts @@ -196,28 +214,33 @@ 1 src/renderer/src/store/repos/repo-removal.ts 1 src/renderer/src/store/repos/repo-update.ts 2 src/renderer/src/store/repos/runtime-repo-catalog-actions.ts +1 src/renderer/src/store/slices/browser/browser-host-state.ts 1 src/renderer/src/store/slices/diff-comment-persistence.ts 1 src/renderer/src/store/slices/editor/actions/markdown-link-action.ts 1 src/renderer/src/store/slices/editor/actions/markdown-preview-actions.ts 1 src/renderer/src/store/slices/editor/actions/open-file-apply.ts 1 src/renderer/src/store/slices/github-cache-key.ts +1 src/renderer/src/store/slices/hosted-review-cache-identity.ts 4 src/renderer/src/store/slices/hosted-review.ts 1 src/renderer/src/store/slices/preflight.ts 1 src/renderer/src/store/slices/project-group-owner-routing.ts +1 src/renderer/src/store/slices/repo-host-identity.ts +1 src/renderer/src/store/slices/repo-reorder-host-split.ts 1 src/renderer/src/store/slices/runtime-status.ts -1 src/renderer/src/store/slices/settings.ts +3 src/renderer/src/store/slices/settings.ts 1 src/renderer/src/store/slices/worktree-visibility-owner-settings.ts 3 src/renderer/src/store/slices/worktree-visibility-settings-write.ts 1 src/renderer/src/store/slices/worktrees/create/create-worktree.ts 1 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts 2 src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts 1 src/renderer/src/store/slices/worktrees/listing/detected-worktree-refresh.ts -1 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts +2 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts +1 src/renderer/src/store/slices/worktrees/listing/worktree-host-ownership.ts 1 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts 2 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts 1 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts 1 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts -3 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts +5 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts 1 src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.ts 1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts 1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index a3b12c1fe097..b7ce9002eb8f 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -8,8 +8,9 @@ import { pathToFileURL } from 'node:url' // instead of by the resource's own host. Per-file counts may only go down. // - Owner routing: the three focus-routing helpers, counted together so renaming one into another // never lowers the count. -// - Focus reads: every read of the setting, including the helpers and `defaultCreationHost`, so -// swapping a helper for a direct read or for the creation default never lowers it either. +// - Focus reads: every read of the setting, including helpers that read it for the caller +// (`defaultCreationHost`, `getSettingsFocusedExecutionHostId`, …), so swapping one form for +// another never lowers it either. const SCAN_ROOT = 'src/renderer/src' const HELPER_NAMES = 'getActiveRuntimeTarget|legacyRouteFromSettings|settingsForRuntimeOwner' @@ -17,22 +18,42 @@ const HELPERS = `(?:${HELPER_NAMES})` const IMPORT_EXPORT_LIST = /\b(?:import|export)\s+(?:type\s+)?\{[^}]*\}/g // Calls and value uses (`.map(helper)`); definitions and type queries are not routing. const FOCUS_ROUTING_USE = new RegExp(`(?` are indexed types (`GlobalSettings['…']`), not reads. +const SETTING_MEMBER_READ = + /\??\.\s*activeRuntimeEnvironmentId\b|(?)\s*)\[\s*['"]activeRuntimeEnvironmentId['"]\s*\]/g +// `const { activeRuntimeEnvironmentId } = s` or `{ activeRuntimeEnvironmentId: id }: T = s`; +// object literals and `({ … }) =>` parameters are not reads of the setting. +const SETTING_DESTRUCTURE_READ = + /\{[^{}]*\bactiveRuntimeEnvironmentId\b[^{}]*\}\s*(?::[^=;{}]*)?=(?![=>])/g + +/** + * Reads of the setting (member, element and destructuring reads) plus every helper that reads it + * on the caller's behalf. Object-literal keys are writes and are not counted. + */ export function countFocusSettingReads(sourceText) { const body = sourceText.replace(IMPORT_EXPORT_LIST, '') return ( - (body.match(FOCUS_SETTING_READ)?.length ?? 0) + - (body.match(DEFAULT_CREATION_HOST_USE)?.length ?? 0) + - countFocusRoutingCalls(sourceText) + (body.match(SETTING_MEMBER_READ)?.length ?? 0) + + (body.match(SETTING_DESTRUCTURE_READ)?.length ?? 0) + + (body.match(FOCUS_READER_USE)?.length ?? 0) ) } @@ -60,8 +81,8 @@ export const RATCHETS = [ baselinePath: 'config/focus-setting-read-baseline.txt', count: countFocusSettingReads, header: [ - '# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the', - '# focus-routing helpers and defaultCreationHost(, per file.', + '# Renderer reads of the Active Server setting (member, element and destructuring reads of', + '# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file.', '# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read', '# the default host, through defaultCreationHost. Everything else routes by the owner.', '# Prune after removing reads: pnpm check:owner-routing-ratchet --prune' diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index f2ec389504f2..dc80801fabc4 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -47,18 +47,40 @@ describe('countFocusSettingReads', () => { "import { defaultCreationHost } from './default-creation-host'", 'const a = settings?.activeRuntimeEnvironmentId', 'const b = state.settings.activeRuntimeEnvironmentId', - 'const c = getActiveRuntimeTarget(settings)', - 'const d = defaultCreationHost(settings)', + "const c = settings['activeRuntimeEnvironmentId']", + 'const d = getActiveRuntimeTarget(settings)', + 'const e = defaultCreationHost(settings)', 'export function defaultCreationHost(settings) {}' ].join('\n') - expect(countFocusSettingReads(src)).toBe(4) + expect(countFocusSettingReads(src)).toBe(5) }) - it('does not count writes, keys or type positions', () => { + it('counts a shared focus helper, so wrapping one in an owner transport lowers nothing', () => { + expect( + countFocusSettingReads( + 'const t = runtimeTargetForOwnerHostId(getSettingsFocusedExecutionHostId(s))' + ) + ).toBe(1) + expect(countFocusSettingReads('const id = getSingleFocusedRuntimeEnvironmentId(state)')).toBe(1) + }) + + it('counts destructuring reads', () => { + expect(countFocusSettingReads('const { activeRuntimeEnvironmentId } = s')).toBe(1) + expect( + countFocusSettingReads( + "const { theme, activeRuntimeEnvironmentId: id }: Pick = s" + ) + ).toBe(1) + }) + + it('does not count writes, keys, props or type positions', () => { const src = [ 'const owner = { activeRuntimeEnvironmentId: id }', + 'settings = { ...settings, activeRuntimeEnvironmentId: null }', "type T = GlobalSettings['activeRuntimeEnvironmentId']", - 'updateSettings({ activeRuntimeEnvironmentId: null })' + 'updateSettings({ activeRuntimeEnvironmentId: null })', + 'const f = ({ activeRuntimeEnvironmentId }) => activeRuntimeEnvironmentId', + 'if (a === b) { x = { activeRuntimeEnvironmentId } }' ].join('\n') expect(countFocusSettingReads(src)).toBe(0) }) @@ -73,6 +95,9 @@ describe('hasFocusRoutingAlias', () => { hasFocusRoutingAlias("export {\n settingsForRuntimeOwner as owner\n} from './target'") ).toBe(true) expect(hasFocusRoutingAlias("import { defaultCreationHost as host } from './d'")).toBe(true) + expect( + hasFocusRoutingAlias("import { getSettingsFocusedExecutionHostId as h } from './e'") + ).toBe(true) expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget } from './rpc'")).toBe(false) }) }) From 119c67c5de5263b6505179f2a3559b7212b0afe0 Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 09:46:51 -0700 Subject: [PATCH 4/7] fix(lint): discover functions that read the default host instead of hand-listing them A hand list let a look-alike such as getAutomationListTarget replace getActiveRuntimeTarget and lower both ratchets. Every exported top-level function whose body reads the setting or calls a seed helper now counts as a reader, and nested destructuring reads count too. --- config/focus-setting-read-baseline.txt | 519 ++++++++++++++---- .../scripts/check-owner-routing-ratchet.mjs | 138 +++-- .../check-owner-routing-ratchet.test.mjs | 46 +- 3 files changed, 546 insertions(+), 157 deletions(-) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 1bfe6042f14b..7f4e6728acaa 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -1,250 +1,543 @@ # Renderer reads of the Active Server setting (member, element and destructuring reads of -# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file. +# activeRuntimeEnvironmentId) plus calls of exported functions that read it, per file. # This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read # the default host, through defaultCreationHost. Everything else routes by the owner. # Prune after removing reads: pnpm check:owner-routing-ratchet --prune -1 src/renderer/src/components/LinearIssueWorkspace.tsx -2 src/renderer/src/components/activity/activity-thread-actions.ts +2 src/renderer/src/app-shell/AppBackgroundServices.tsx +1 src/renderer/src/components/Landing.tsx +3 src/renderer/src/components/LinearIssueWorkspace.tsx +1 src/renderer/src/components/NativeChatResumeOnRestartModal.tsx +3 src/renderer/src/components/NewWorkspaceComposerModal.tsx +2 src/renderer/src/components/QuickOpen.tsx +3 src/renderer/src/components/activity/activity-thread-actions.ts 1 src/renderer/src/components/activity/use-agent-pane-threads.ts -2 src/renderer/src/components/agent/AgentSettingsDialog.tsx +4 src/renderer/src/components/agent/AgentSettingsDialog.tsx 1 src/renderer/src/components/automations/automation-host-client.ts +1 src/renderer/src/components/automations/use-automation-source-host-availability.ts +1 src/renderer/src/components/automations/use-automations-page-controller.ts +1 src/renderer/src/components/automations/use-automations-page-destination-form.ts 2 src/renderer/src/components/automations/use-automations-page-setup-state.ts +1 src/renderer/src/components/browser-pane/browser-reopen-on-server.ts +1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-stream.ts 1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-stream-activation.ts -1 src/renderer/src/components/cmd-j/quick-action-context.ts +2 src/renderer/src/components/cmd-j/quick-action-context.ts +1 src/renderer/src/components/editor/EditorAutosaveController.tsx +2 src/renderer/src/components/editor/EditorContent.tsx +1 src/renderer/src/components/editor/EditorDiffFileSurface.tsx +1 src/renderer/src/components/editor/EditorEditFileSurface.tsx +1 src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx 2 src/renderer/src/components/editor/EditorPanel.tsx -1 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx -1 src/renderer/src/components/editor/ExternalFileChangeCompareDialog.tsx -1 src/renderer/src/components/editor/combined-diff/load-sections/fetch-combined-diff-section.ts +6 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx +11 src/renderer/src/components/editor/ExternalFileChangeCompareDialog.tsx +1 src/renderer/src/components/editor/MonacoGutterContextMenu.tsx +4 src/renderer/src/components/editor/combined-diff/load-sections/fetch-combined-diff-section.ts +1 src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-loader.ts 2 src/renderer/src/components/editor/csv/CsvPagedViewer.tsx -4 src/renderer/src/components/editor/editor-dropped-file-open.ts -3 src/renderer/src/components/editor/editor-restored-tab-conflict-scan.ts -1 src/renderer/src/components/editor/markdown-document-list-request.ts -1 src/renderer/src/components/editor/markdown-preview-link-actions.ts +1 src/renderer/src/components/editor/csv/csv-file-content.ts +1 src/renderer/src/components/editor/csv/csv-link-routing.ts +6 src/renderer/src/components/editor/editor-dropped-file-open.ts +6 src/renderer/src/components/editor/editor-restored-tab-conflict-scan.ts +1 src/renderer/src/components/editor/editor-save-queue.ts +1 src/renderer/src/components/editor/local-image-src-reader.ts +3 src/renderer/src/components/editor/markdown-document-list-request.ts +2 src/renderer/src/components/editor/markdown-preview-link-actions.ts +1 src/renderer/src/components/editor/markdown-preview-links.ts +1 src/renderer/src/components/editor/markdown-preview-local-images.ts +1 src/renderer/src/components/editor/markdown-preview-system-link-action.ts 1 src/renderer/src/components/editor/restored-editor-workspace-runtime-owner.ts +1 src/renderer/src/components/editor/rich-markdown-editor-config.ts 2 src/renderer/src/components/editor/rich-markdown-image-context.ts -2 src/renderer/src/components/editor/rich-markdown-image-insert.ts -2 src/renderer/src/components/editor/rich-markdown-paste-image.ts +3 src/renderer/src/components/editor/rich-markdown-image-insert.ts +3 src/renderer/src/components/editor/rich-markdown-paste-image.ts +1 src/renderer/src/components/editor/use-markdown-preview-components.tsx +1 src/renderer/src/components/editor/use-markdown-preview-foundation.ts 1 src/renderer/src/components/editor/use-markdown-preview-source-foundation.ts -1 src/renderer/src/components/editor/useEditorPanelDiffContentLoader.ts -3 src/renderer/src/components/editor/useEditorPanelFileContentLoader.ts -1 src/renderer/src/components/editor/useLocalImageSrc.ts -3 src/renderer/src/components/editor/useMarkdownDocuments.ts -1 src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx -1 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-panel.tsx -2 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-request-actions.ts +2 src/renderer/src/components/editor/useEditorPanelActiveTabContentLoad.ts +2 src/renderer/src/components/editor/useEditorPanelContentReloadTriggers.ts +4 src/renderer/src/components/editor/useEditorPanelContentState.ts +5 src/renderer/src/components/editor/useEditorPanelDiffContentLoader.ts +6 src/renderer/src/components/editor/useEditorPanelFileContentLoader.ts +1 src/renderer/src/components/editor/useLocalImagePick.ts +4 src/renderer/src/components/editor/useLocalImageSrc.ts +5 src/renderer/src/components/editor/useMarkdownDocuments.ts +1 src/renderer/src/components/editor/useRichMarkdownProgrammaticSync.ts +1 src/renderer/src/components/editor/useRichMarkdownReconcileRoundTrip.ts +1 src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx +1 src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx +2 src/renderer/src/components/feature-wall/use-feature-wall-task-source-presentation.ts +2 src/renderer/src/components/feature-wall/use-integration-connection-status.ts +3 src/renderer/src/components/floating-terminal/floating-terminal-panel-test-harness.ts +3 src/renderer/src/components/floating-terminal/floating-terminal-panel-test-module-mocks.ts +1 src/renderer/src/components/floating-terminal/use-floating-terminal-panel-store-state.ts +2 src/renderer/src/components/github-item-dialog/discuss-item/conversation-tab-pr-sidebar.tsx +1 src/renderer/src/components/github-item-dialog/discuss-item/conversation-tab.tsx +4 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts +4 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx +2 src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx +5 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-panel.tsx +6 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-request-actions.ts +1 src/renderer/src/components/github-project/ProjectCellIdentity.tsx 1 src/renderer/src/components/github-project/ProjectCellIssueType.tsx +2 src/renderer/src/components/github-project/ProjectCellRepositoryEditors.tsx +2 src/renderer/src/components/github-project/ProjectItemSlugDialog.tsx +2 src/renderer/src/components/github-project/ProjectPicker.tsx +3 src/renderer/src/components/github-project/ProjectViewWrapper.tsx 4 src/renderer/src/components/github-project/project-picker-runtime.ts +1 src/renderer/src/components/github-project/slug-dialog/AssigneesEditor.tsx 1 src/renderer/src/components/github-project/slug-dialog/Comments.tsx -1 src/renderer/src/components/github-project/slug-dialog/SlugDialogBody.tsx -1 src/renderer/src/components/github/github-rate-limit-display.tsx -4 src/renderer/src/components/github/github-work-item-edit-mutations.ts -1 src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx +1 src/renderer/src/components/github-project/slug-dialog/LabelsEditor.tsx +9 src/renderer/src/components/github-project/slug-dialog/SlugDialogBody.tsx +2 src/renderer/src/components/github-project/useProjectPickerBrowse.ts +2 src/renderer/src/components/github-project/useProjectViewTable.ts +3 src/renderer/src/components/github/PRAssigneesPanel.tsx +2 src/renderer/src/components/github/PRFilterDropdowns.tsx +3 src/renderer/src/components/github/github-rate-limit-display.tsx +5 src/renderer/src/components/github/github-work-item-edit-mutations.ts +3 src/renderer/src/components/gitlab/gitlab-rate-limit-display.tsx +1 src/renderer/src/components/jira-connect-dialog.tsx 1 src/renderer/src/components/landing-preflight-runtime.ts 1 src/renderer/src/components/linear-api-key-dialog.tsx -2 src/renderer/src/components/native-chat/native-chat-file-link-existence.ts -1 src/renderer/src/components/new-workspace/use-linear-source-connection.ts +5 src/renderer/src/components/linear-issue-activity.tsx +2 src/renderer/src/components/linear-issue-clipboard.ts +9 src/renderer/src/components/linear-issue-project-selector.tsx +7 src/renderer/src/components/linear-issue-sub-issues.tsx +14 src/renderer/src/components/linear-issue-workspace-header.tsx +8 src/renderer/src/components/linear-issue-workspace-sidebar.tsx +1 src/renderer/src/components/native-chat/NativeChatTranscriptChrome.tsx +1 src/renderer/src/components/native-chat/NativeChatVisualFrame.tsx +1 src/renderer/src/components/native-chat/claude-model-switch-confirmation.ts +4 src/renderer/src/components/native-chat/native-chat-file-link-existence.ts +1 src/renderer/src/components/native-chat/native-chat-observed-send.ts +4 src/renderer/src/components/native-chat/native-chat-runtime-send.ts +2 src/renderer/src/components/native-chat/native-chat-session-option-discovery.ts +1 src/renderer/src/components/native-chat/use-native-chat-file-link-click.ts +1 src/renderer/src/components/native-chat/use-native-chat-file-link-existence.tsx +1 src/renderer/src/components/native-chat/use-native-chat-interactive-send.ts +1 src/renderer/src/components/native-chat/use-native-chat-mention-files.ts +1 src/renderer/src/components/new-workspace/smart-workspace-repo-slug.ts +1 src/renderer/src/components/new-workspace/use-jira-source-connection.ts +2 src/renderer/src/components/new-workspace/use-linear-source-connection.ts +1 src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts +2 src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts +1 src/renderer/src/components/onboarding/IntegrationsStep.tsx 1 src/renderer/src/components/ports/WorkspacePortScanner.tsx +1 src/renderer/src/components/pull-request-page/actions/merge-actions.ts 1 src/renderer/src/components/pull-request-page/actions/panel.tsx -1 src/renderer/src/components/pull-request-page/reviewers/panel.tsx -2 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts -1 src/renderer/src/components/quick-open-file-list.ts +4 src/renderer/src/components/pull-request-page/conversation/tab.tsx +3 src/renderer/src/components/pull-request-page/edit/issue-updates.ts +4 src/renderer/src/components/pull-request-page/edit/section.tsx +4 src/renderer/src/components/pull-request-page/reviewers/panel.tsx +6 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts +2 src/renderer/src/components/quick-open-file-list-test-harness.ts +5 src/renderer/src/components/quick-open-file-list.ts 2 src/renderer/src/components/quick-open-file-navigation.ts +2 src/renderer/src/components/quick-open-recent-validation.ts +3 src/renderer/src/components/repo/CreateFromPicker.tsx +1 src/renderer/src/components/right-sidebar/FileExplorer.tsx +1 src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.tsx +1 src/renderer/src/components/right-sidebar/ai-vault-original-pane-actions.ts +1 src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts +3 src/renderer/src/components/right-sidebar/checks-panel-hosted-review-click-routing.ts +1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-check-and-review-actions.tsx +1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-create-review.tsx +2 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-generation.tsx +3 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-git-status-effects.tsx +3 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-manual-refresh.tsx +1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-review-state.tsx 2 src/renderer/src/components/right-sidebar/file-explorer-deletion-editors.ts -1 src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +2 src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +5 src/renderer/src/components/right-sidebar/git-status-refresh.ts +1 src/renderer/src/components/right-sidebar/local-workspace-ports-panel.tsx 1 src/renderer/src/components/right-sidebar/push-target-upstream-refresh-cache.ts -2 src/renderer/src/components/right-sidebar/source-control/commit/use-entry-mutations.ts -1 src/renderer/src/components/right-sidebar/source-control/listing/use-submodule-status.ts +1 src/renderer/src/components/right-sidebar/source-control/ai/use-ai.ts +5 src/renderer/src/components/right-sidebar/source-control/commit/use-bulk-actions.ts +1 src/renderer/src/components/right-sidebar/source-control/commit/use-commit-action.ts +2 src/renderer/src/components/right-sidebar/source-control/commit/use-commit-message-generation.ts +1 src/renderer/src/components/right-sidebar/source-control/commit/use-discard-confirmation.ts +7 src/renderer/src/components/right-sidebar/source-control/commit/use-entry-mutations.ts +2 src/renderer/src/components/right-sidebar/source-control/listing/use-file-listing.ts +2 src/renderer/src/components/right-sidebar/source-control/listing/use-submodule-status.ts 1 src/renderer/src/components/right-sidebar/source-control/listing/use-worktree-context.ts -1 src/renderer/src/components/right-sidebar/source-control/sync/use-git-history.ts +1 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-model.ts +2 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-state.ts +1 src/renderer/src/components/right-sidebar/source-control/review/create-pr-intent-run-snapshot.ts +1 src/renderer/src/components/right-sidebar/source-control/review/use-create-pr-intent-commit-message.ts +1 src/renderer/src/components/right-sidebar/source-control/review/use-create-pr-intent-probes.ts +1 src/renderer/src/components/right-sidebar/source-control/review/use-create-pr-intent-review.ts +2 src/renderer/src/components/right-sidebar/source-control/review/use-pull-request-generation.ts +1 src/renderer/src/components/right-sidebar/source-control/sync/git-history-commit-context-menu.tsx +1 src/renderer/src/components/right-sidebar/source-control/sync/use-base-ref-default.ts +1 src/renderer/src/components/right-sidebar/source-control/sync/use-branch-compare.ts +2 src/renderer/src/components/right-sidebar/source-control/sync/use-conflict-abort.ts +2 src/renderer/src/components/right-sidebar/source-control/sync/use-git-history-commit-actions.ts +2 src/renderer/src/components/right-sidebar/source-control/sync/use-git-history.ts 1 src/renderer/src/components/right-sidebar/source-control/sync/use-status-refresh.ts -1 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts -1 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts +1 src/renderer/src/components/right-sidebar/stale-conflict-operation-poll.ts +2 src/renderer/src/components/right-sidebar/use-create-pull-request-base-ref-discovery.ts +2 src/renderer/src/components/right-sidebar/use-create-pull-request-field-generation.ts +2 src/renderer/src/components/right-sidebar/use-create-pull-request-field-seeding.ts +1 src/renderer/src/components/right-sidebar/use-file-explorer-ignored-paths.ts +1 src/renderer/src/components/right-sidebar/use-file-explorer-name-filter.ts +2 src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts +3 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts +2 src/renderer/src/components/right-sidebar/useFileDeletion.ts +2 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts +1 src/renderer/src/components/right-sidebar/useFileExplorerImport.ts 1 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts -1 src/renderer/src/components/settings/BrowserPane.tsx +1 src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts +3 src/renderer/src/components/right-sidebar/useFileSearchRunner.ts +1 src/renderer/src/components/right-sidebar/useGitStatusPolling.ts +3 src/renderer/src/components/settings/BaseRefPicker.tsx +2 src/renderer/src/components/settings/BrowserPane.tsx 1 src/renderer/src/components/settings/CliSkillRuntimeSetup.tsx -1 src/renderer/src/components/settings/GeneralPane.tsx -2 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx -1 src/renderer/src/components/settings/RepositoryIconPicker.tsx -3 src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx +1 src/renderer/src/components/settings/CommitMessageAiPane.tsx +16 src/renderer/src/components/settings/GeneralPane.tsx +2 src/renderer/src/components/settings/LinearAgentSkillPane.tsx +1 src/renderer/src/components/settings/RepositoryForkSyncSection.tsx +20 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx +4 src/renderer/src/components/settings/RepositoryIconColorSection.tsx +3 src/renderer/src/components/settings/RepositoryIconEmojiPicker.tsx +5 src/renderer/src/components/settings/RepositoryIconPicker.tsx +13 src/renderer/src/components/settings/RepositoryIconTabs.tsx +4 src/renderer/src/components/settings/RepositoryPane.tsx +4 src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx +1 src/renderer/src/components/settings/Settings.tsx +1 src/renderer/src/components/settings/ShortcutsPane.tsx +1 src/renderer/src/components/settings/TaskSourceLinearSetup.tsx +1 src/renderer/src/components/settings/bitbucket-credentials-dialog.tsx +1 src/renderer/src/components/settings/cli-source-control-integration-cards.tsx +3 src/renderer/src/components/settings/general-search.ts +3 src/renderer/src/components/settings/jira-integration-card.tsx 1 src/renderer/src/components/settings/linear-agent-skill-install-cta.tsx +1 src/renderer/src/components/settings/mcp-config-inspection.ts 2 src/renderer/src/components/settings/provider-account-scope.ts -1 src/renderer/src/components/settings/use-runtime-environment-mutation-actions.ts -1 src/renderer/src/components/settings/use-settings-store-model.ts +9 src/renderer/src/components/settings/runtime-active-server-section.tsx +11 src/renderer/src/components/settings/runtime-environment-dialogs.tsx +22 src/renderer/src/components/settings/runtime-environment-host-details.ts +8 src/renderer/src/components/settings/runtime-server-row.tsx +20 src/renderer/src/components/settings/runtime-server-workflow-sections.tsx +6 src/renderer/src/components/settings/runtime-servers-connect-section.tsx +1 src/renderer/src/components/settings/settings-capability-section-renderers.tsx +2 src/renderer/src/components/settings/settings-remote-security-section-renderers.tsx +2 src/renderer/src/components/settings/settings-setup-workflow-section-renderers.tsx +1 src/renderer/src/components/settings/source-control-preflight-card-status.ts +4 src/renderer/src/components/settings/task-tracker-integration-cards.tsx +2 src/renderer/src/components/settings/use-integration-provider-status-refresh.ts +3 src/renderer/src/components/settings/use-repository-issue-command.ts +1 src/renderer/src/components/settings/use-runtime-environment-catalog.ts +3 src/renderer/src/components/settings/use-runtime-environment-connection-actions.ts +7 src/renderer/src/components/settings/use-runtime-environment-mutation-actions.ts +2 src/renderer/src/components/settings/use-settings-navigation-model.ts +2 src/renderer/src/components/settings/use-settings-store-model.ts +2 src/renderer/src/components/settings/use-task-source-provider-readiness.ts 1 src/renderer/src/components/setup-guide/setup-guide-progress-readiness.ts -2 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +3 src/renderer/src/components/setup-guide/use-setup-guide-progress.ts +11 src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx +10 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +1 src/renderer/src/components/sidebar/SidebarTaskNavButton.tsx +1 src/renderer/src/components/sidebar/WorktreeCardAgents.tsx 1 src/renderer/src/components/sidebar/WorktreeList.tsx 1 src/renderer/src/components/sidebar/add-repo-runtime-owner.ts -1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts -1 src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts +3 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts +8 src/renderer/src/components/sidebar/linear-agent-skill-setup-copy.ts +1 src/renderer/src/components/sidebar/linear-agent-skill-setup-reminder-toast.ts +2 src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts 2 src/renderer/src/components/sidebar/sidebar-host-options.ts 1 src/renderer/src/components/sidebar/sidebar-project-drop.ts 1 src/renderer/src/components/sidebar/use-add-repo-host-selection.ts +1 src/renderer/src/components/sidebar/use-delete-worktree-status-hydration.ts +1 src/renderer/src/components/sidebar/use-sidebar-host-scope-options.ts 1 src/renderer/src/components/sidebar/use-visible-workspace-kanban-worktree-ids.ts +1 src/renderer/src/components/sidebar/useAddRepoCloneFlow.ts +1 src/renderer/src/components/sidebar/useCreateRepo.ts +2 src/renderer/src/components/sidebar/useSidebarProjectDrop.ts 1 src/renderer/src/components/sidebar/visible-worktree-options-from-state.ts 2 src/renderer/src/components/sidebar/visible-worktrees.ts -1 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx +1 src/renderer/src/components/sidebar/workspace-reference-details.ts +1 src/renderer/src/components/sidebar/worktree-card-parent-content.tsx +3 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx 1 src/renderer/src/components/sidebar/worktree-filter-visibility.ts 2 src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts 2 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts -5 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx -1 src/renderer/src/components/status-bar/SshStatusSegment.tsx +1 src/renderer/src/components/sidebar/worktree-list/listing/use-section-rows.ts +1 src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx +1 src/renderer/src/components/skills/SkillsPage.tsx +7 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx +1 src/renderer/src/components/status-bar/CodexSwitcherMenu.tsx +9 src/renderer/src/components/status-bar/RuntimeHostStatusRow.tsx +5 src/renderer/src/components/status-bar/SshStatusSegment.tsx +3 src/renderer/src/components/status-bar/SshTargetStatusRow.tsx +1 src/renderer/src/components/status-bar/StatusBar.tsx +5 src/renderer/src/components/status-bar/StatusBarSurface.tsx 2 src/renderer/src/components/status-bar/provider-account-sync-key.ts +2 src/renderer/src/components/status-bar/remote-host-connection-status.ts +1 src/renderer/src/components/status-bar/runtime-environment-explicit-connect.ts +5 src/renderer/src/components/status-bar/ssh-status-segment-copy.ts 1 src/renderer/src/components/status-bar/status-bar-claude-accounts.ts 1 src/renderer/src/components/status-bar/status-bar-codex-accounts.ts 1 src/renderer/src/components/status-bar/usage-provider-settings-target.ts -5 src/renderer/src/components/status-bar/use-codex-switcher-controller.ts -3 src/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-test-harness.ts -1 src/renderer/src/components/tab-bar/tab-create-entry-local-path.ts -1 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts -1 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts +7 src/renderer/src/components/status-bar/use-codex-switcher-controller.ts +1 src/renderer/src/components/status-bar/use-status-bar-controller.ts +1 src/renderer/src/components/status-bar/use-workspace-space-git-refresh-action.ts +1 src/renderer/src/components/tab-bar/TabBar.tsx +1 src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx +4 src/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-test-harness.ts +1 src/renderer/src/components/tab-bar/tab-create-entry-action.ts +2 src/renderer/src/components/tab-bar/tab-create-entry-local-path.ts +2 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts +3 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts +1 src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts 1 src/renderer/src/components/tab-bar/windows-shell-menu-visibility.ts -2 src/renderer/src/components/task-page-github-reviewer-actions.ts -1 src/renderer/src/components/task-page/github/AssigneesCell.tsx +1 src/renderer/src/components/tab-group/AiVaultSessionDropLayer.tsx +1 src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts +2 src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts +6 src/renderer/src/components/task-page-github-reviewer-actions.ts +1 src/renderer/src/components/task-page/TaskPage.tsx +2 src/renderer/src/components/task-page/github/AssigneesCell.tsx 2 src/renderer/src/components/task-page/github/MergeCell.tsx +2 src/renderer/src/components/task-page/github/ReviewCell.tsx +3 src/renderer/src/components/task-page/github/Rows.tsx 1 src/renderer/src/components/task-page/github/StatusCell.tsx -2 src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts -1 src/renderer/src/components/terminal-pane/terminal-file-path-mapping.ts +1 src/renderer/src/components/task-page/linear/ConnectDialog.tsx +3 src/renderer/src/components/task-page/linear/Content.tsx +2 src/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.ts +1 src/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.ts +1 src/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.ts +6 src/renderer/src/components/terminal-pane/terminal-file-link-actions.ts +1 src/renderer/src/components/terminal-pane/terminal-file-link-hit-testing.ts +1 src/renderer/src/components/terminal-pane/terminal-file-link-target.ts +4 src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts +3 src/renderer/src/components/terminal-pane/terminal-file-path-mapping.ts 1 src/renderer/src/components/terminal-pane/terminal-fit-restore.ts 1 src/renderer/src/components/terminal-pane/terminal-host-workspace-file.ts +2 src/renderer/src/components/terminal-pane/terminal-link-handlers.ts 1 src/renderer/src/components/terminal-pane/terminal-link-open-hints.ts +1 src/renderer/src/components/terminal-pane/terminal-native-file-drop.ts +1 src/renderer/src/components/terminal-pane/terminal-osc-link-routing.ts +1 src/renderer/src/components/terminal-pane/terminal-pane-mount-preparation.ts 2 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts -1 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts -1 src/renderer/src/components/use-task-page-github-issue-draft.ts +2 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts +1 src/renderer/src/components/terminal/pty-running-work-probe.ts +2 src/renderer/src/components/use-quick-open-interaction.ts +3 src/renderer/src/components/use-task-page-github-issue-draft.ts 1 src/renderer/src/components/use-task-page-source-availability.ts -1 src/renderer/src/components/use-worktree-jump-palette-filter.ts -2 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts -3 src/renderer/src/hooks/composer-state/attachment-drop-state.ts +2 src/renderer/src/components/use-task-page-store-bindings.ts +4 src/renderer/src/components/use-terminal-create-actions.ts +1 src/renderer/src/components/use-worktree-jump-palette-controller.ts +2 src/renderer/src/components/use-worktree-jump-palette-filter.ts +3 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts +2 src/renderer/src/components/use-worktree-jump-palette-store-state.ts +2 src/renderer/src/hooks/automation-dispatch-handler.ts +1 src/renderer/src/hooks/automation-dispatch-workspace.ts +4 src/renderer/src/hooks/composer-state/attachment-drop-state.ts +1 src/renderer/src/hooks/composer-state/composer-attachment-drop-state.ts 1 src/renderer/src/hooks/composer-state/composer-navigation-actions.ts +2 src/renderer/src/hooks/composer-state/composer-source-state.ts +1 src/renderer/src/hooks/composer-state/composer-submit-orchestration.ts +2 src/renderer/src/hooks/composer-state/composer-target-state.ts +1 src/renderer/src/hooks/composer-state/github-provider-selection.ts +2 src/renderer/src/hooks/composer-state/github-submit-resolution.ts 1 src/renderer/src/hooks/composer-state/gitlab-provider-selection.ts 1 src/renderer/src/hooks/composer-state/provider-runtime-sync.ts 1 src/renderer/src/hooks/composer-state/quick-creation-execution.ts 1 src/renderer/src/hooks/composer-state/workspace-identity-state.ts +1 src/renderer/src/hooks/editor-external-watch-disk-verification.ts 1 src/renderer/src/hooks/editor-external-watch-targets.ts -1 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts +1 src/renderer/src/hooks/editor-runtime-file-watch.ts +1 src/renderer/src/hooks/ipc-events-terminal-create-test-harness.ts +1 src/renderer/src/hooks/ipc-events-test-harness.ts +2 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts 1 src/renderer/src/hooks/ipc-events/settings-sidebar-ipc-bridge.ts +26 src/renderer/src/hooks/settings-navigation-capability-sections.ts +10 src/renderer/src/hooks/settings-navigation-interface-sections.ts +25 src/renderer/src/hooks/settings-navigation-remote-sections.ts +23 src/renderer/src/hooks/settings-navigation-workflow-sections.ts 3 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts +1 src/renderer/src/hooks/useActiveProjectSkillRuntime.ts +1 src/renderer/src/hooks/useEditorExternalWatch.ts 2 src/renderer/src/hooks/useGitHubRepoMetadata.ts 4 src/renderer/src/hooks/useGitHubSlugMetadata.ts +1 src/renderer/src/hooks/useInstalledAgentSkills.ts 1 src/renderer/src/hooks/useIssueMetadata.ts -2 src/renderer/src/hooks/useSettingsNavigationMetadata.ts -1 src/renderer/src/hooks/useWindowsTerminalCapabilityOwnerKey.ts -3 src/renderer/src/lib/activate-ai-vault-structured-session.ts +1 src/renderer/src/hooks/useLinearProviderConnected.ts +3 src/renderer/src/hooks/useSettingsNavigationMetadata.ts +2 src/renderer/src/hooks/useWindowsTerminalCapabilityOwnerKey.ts +7 src/renderer/src/lib/activate-ai-vault-structured-session.ts 1 src/renderer/src/lib/active-agent-note-send.ts 1 src/renderer/src/lib/active-agent-note-target.ts +1 src/renderer/src/lib/agent-draft-paste-content.ts +1 src/renderer/src/lib/agent-draft-readiness.ts +2 src/renderer/src/lib/agent-followup-delivery.ts 1 src/renderer/src/lib/agent-hibernation-coordinator-test-fixture.ts +3 src/renderer/src/lib/agent-paste-draft.ts +1 src/renderer/src/lib/agent-ready-wait.ts 1 src/renderer/src/lib/automation-session-observer.ts -1 src/renderer/src/lib/client-creation-action-policy.ts -1 src/renderer/src/lib/codex-pane-selection-lane.ts +1 src/renderer/src/lib/client-creation-action-error.ts +2 src/renderer/src/lib/client-creation-action-policy.ts +4 src/renderer/src/lib/codex-pane-selection-lane.ts +2 src/renderer/src/lib/codex-session-restart.ts 1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host +3 src/renderer/src/lib/ensure-hooks-confirmed.ts +2 src/renderer/src/lib/file-preview.ts 1 src/renderer/src/lib/file-search-result-owner.ts 2 src/renderer/src/lib/folder-workspace-runtime-owner.ts 1 src/renderer/src/lib/github-pr-start-point.ts 1 src/renderer/src/lib/github-source-runtime-context.ts +2 src/renderer/src/lib/github-work-item-source-lookup.ts 1 src/renderer/src/lib/hosted-review-repo-read.ts +6 src/renderer/src/lib/http-link-destinations.ts 3 src/renderer/src/lib/http-link-routing.ts -1 src/renderer/src/lib/launch-agent-background-session.ts +2 src/renderer/src/lib/launch-agent-background-session.ts +1 src/renderer/src/lib/launch-work-item-direct-preflight.ts 1 src/renderer/src/lib/launch-worktree-background-terminals.ts -2 src/renderer/src/lib/local-file-access.ts -1 src/renderer/src/lib/local-preflight-context.ts +3 src/renderer/src/lib/local-file-access.ts +2 src/renderer/src/lib/local-preflight-context.ts +1 src/renderer/src/lib/markdown-document-templates.ts +1 src/renderer/src/lib/open-agent-message-sender.ts 2 src/renderer/src/lib/provider-runtime-context.ts -2 src/renderer/src/lib/repo-runtime-owner.ts -1 src/renderer/src/lib/repo-slug-cache.ts -1 src/renderer/src/lib/repo-slug-index.ts -1 src/renderer/src/lib/resolve-owner.ts +1 src/renderer/src/lib/remap-open-editor-tabs-for-path-change.ts +4 src/renderer/src/lib/repo-runtime-owner.ts +3 src/renderer/src/lib/repo-slug-cache.ts +5 src/renderer/src/lib/repo-slug-index.ts +2 src/renderer/src/lib/resolve-owner.ts 1 src/renderer/src/lib/runtime-session-mirror-owners.ts +1 src/renderer/src/lib/runtime-session-mirror-targets.ts 1 src/renderer/src/lib/single-runtime-legacy-owner.ts -1 src/renderer/src/lib/structured-agent-session-tab-activation.ts +2 src/renderer/src/lib/structured-agent-session-tab-activation.ts 2 src/renderer/src/lib/terminal-worktree-route.ts 3 src/renderer/src/lib/unread-badge-count-selector.ts -1 src/renderer/src/lib/workspace-file-host-routing.ts +4 src/renderer/src/lib/workspace-browser-tab-open.ts +3 src/renderer/src/lib/workspace-file-host-routing.ts +1 src/renderer/src/lib/worktree-activation.ts 1 src/renderer/src/lib/worktree-creation-flow-startup.ts +1 src/renderer/src/lib/worktree-creation-flow.ts 1 src/renderer/src/lib/worktree-operation-route.ts 1 src/renderer/src/lib/worktree-runtime-owner.ts -1 src/renderer/src/runtime/mobile-markdown-bridge.ts +3 src/renderer/src/runtime/mobile-markdown-bridge.ts 1 src/renderer/src/runtime/runtime-client-target.ts -1 src/renderer/src/runtime/runtime-file-import-client.ts -1 src/renderer/src/runtime/runtime-file-metadata-client.ts -1 src/renderer/src/runtime/runtime-file-mutation-client.ts -1 src/renderer/src/runtime/runtime-file-range-client.ts -2 src/renderer/src/runtime/runtime-file-read-client.ts -4 src/renderer/src/runtime/runtime-file-routing.ts -4 src/renderer/src/runtime/runtime-file-search-client.ts +2 src/renderer/src/runtime/runtime-file-import-client.ts +3 src/renderer/src/runtime/runtime-file-metadata-client.ts +8 src/renderer/src/runtime/runtime-file-mutation-client.ts +2 src/renderer/src/runtime/runtime-file-range-client.ts +10 src/renderer/src/runtime/runtime-file-read-client.ts +6 src/renderer/src/runtime/runtime-file-routing.ts +5 src/renderer/src/runtime/runtime-file-search-client.ts +1 src/renderer/src/runtime/runtime-file-upload-client.ts 1 src/renderer/src/runtime/runtime-file-watch-client.ts -1 src/renderer/src/runtime/runtime-git-client-context.ts +2 src/renderer/src/runtime/runtime-git-client-context.ts 5 src/renderer/src/runtime/runtime-git-diff-client.ts 5 src/renderer/src/runtime/runtime-git-generation-client.ts 6 src/renderer/src/runtime/runtime-git-status-client.ts 10 src/renderer/src/runtime/runtime-git-sync-client.ts 9 src/renderer/src/runtime/runtime-git-working-tree-client.ts 3 src/renderer/src/runtime/runtime-hooks-client.ts +17 src/renderer/src/runtime/runtime-jira-client.ts +2 src/renderer/src/runtime/runtime-jira-summary-client.ts 1 src/renderer/src/runtime/runtime-jira-target.ts -1 src/renderer/src/runtime/runtime-linear-client.ts +4 src/renderer/src/runtime/runtime-jira-user-fields-client.ts +8 src/renderer/src/runtime/runtime-linear-client.ts +5 src/renderer/src/runtime/runtime-linear-issue-mutations.ts +12 src/renderer/src/runtime/runtime-linear-project-client.ts +1 src/renderer/src/runtime/runtime-path-existence-batch.ts 3 src/renderer/src/runtime/runtime-repo-client.ts 3 src/renderer/src/runtime/runtime-terminal-inspection.ts 1 src/renderer/src/runtime/runtime-terminal-stream.ts 1 src/renderer/src/runtime/runtime-terminal-verified-input.ts -3 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts -1 src/renderer/src/runtime/web-runtime-browser-creation.ts +5 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts +2 src/renderer/src/runtime/web-runtime-browser-creation.ts 1 src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts 1 src/renderer/src/runtime/web-runtime-session-tab-move.ts 1 src/renderer/src/runtime/web-runtime-terminal-create-operation.ts +3 src/renderer/src/runtime/web-runtime-terminal-creation.ts 1 src/renderer/src/runtime/web-runtime-worktree-activation.ts -1 src/renderer/src/runtime/web-session-tabs-sync/global-session-subscription.ts +2 src/renderer/src/runtime/web-session-tabs-sync/global-session-subscription.ts 1 src/renderer/src/runtime/web-session-tabs-sync/tracking-decisions.ts -1 src/renderer/src/store/folder-workspaces/folder-path-status-actions.ts +2 src/renderer/src/runtime/web-session-tabs-sync/use-web-session-tabs-sync.ts +2 src/renderer/src/store/folder-workspaces/folder-path-status-actions.ts 2 src/renderer/src/store/folder-workspaces/folder-workspace-catalog-actions.ts 3 src/renderer/src/store/folder-workspaces/folder-workspace-mutations.ts 1 src/renderer/src/store/folder-workspaces/folder-workspace-routing.ts 1 src/renderer/src/store/github/cache-identity.ts -1 src/renderer/src/store/github/project-actions.ts -1 src/renderer/src/store/github/project-field-mutations.ts +3 src/renderer/src/store/github/project-actions.ts +2 src/renderer/src/store/github/project-field-mutations.ts 2 src/renderer/src/store/github/project-row-actions.ts -2 src/renderer/src/store/github/repository-routing.ts +1 src/renderer/src/store/github/pull-request-execution.ts +3 src/renderer/src/store/github/repository-routing.ts 1 src/renderer/src/store/github/visible-hosted-review-refresh-targets.ts 1 src/renderer/src/store/github/work-item-mutation-actions.ts +1 src/renderer/src/store/github/work-item-routing.ts +4 src/renderer/src/store/index.ts 6 src/renderer/src/store/project-groups/nested-repository-operations.ts 2 src/renderer/src/store/project-groups/project-group-catalog-actions.ts -4 src/renderer/src/store/project-groups/project-group-mutations.ts +7 src/renderer/src/store/project-groups/project-group-mutations.ts 1 src/renderer/src/store/projects/project-host-routing.ts +1 src/renderer/src/store/projects/project-update.ts 2 src/renderer/src/store/repos/all-host-repo-catalog-actions.ts 1 src/renderer/src/store/repos/owner-routing.ts 2 src/renderer/src/store/repos/repo-add-actions.ts 2 src/renderer/src/store/repos/repo-catalog-actions.ts -1 src/renderer/src/store/repos/repo-removal.ts -1 src/renderer/src/store/repos/repo-update.ts +2 src/renderer/src/store/repos/repo-removal.ts +2 src/renderer/src/store/repos/repo-update.ts 2 src/renderer/src/store/repos/runtime-repo-catalog-actions.ts +2 src/renderer/src/store/repos/safe-auto-fork-sync.ts 1 src/renderer/src/store/slices/browser/browser-host-state.ts +2 src/renderer/src/store/slices/browser/browser-tab-actions.ts +2 src/renderer/src/store/slices/browser/paired-browser-tab-creator.ts 1 src/renderer/src/store/slices/diff-comment-persistence.ts -1 src/renderer/src/store/slices/editor/actions/markdown-link-action.ts +4 src/renderer/src/store/slices/editor/actions/git-remote-push-pull.ts +1 src/renderer/src/store/slices/editor/actions/git-remote-status.ts +8 src/renderer/src/store/slices/editor/actions/git-remote-sync.ts +2 src/renderer/src/store/slices/editor/actions/markdown-link-action.ts 1 src/renderer/src/store/slices/editor/actions/markdown-preview-actions.ts +1 src/renderer/src/store/slices/editor/actions/open-file-action.ts 1 src/renderer/src/store/slices/editor/actions/open-file-apply.ts +2 src/renderer/src/store/slices/editor/create-editor-slice.ts +1 src/renderer/src/store/slices/editor/tabs/untitled-file-cleanup.ts 1 src/renderer/src/store/slices/github-cache-key.ts +1 src/renderer/src/store/slices/github-slice-test-harness.ts +3 src/renderer/src/store/slices/github.ts 1 src/renderer/src/store/slices/hosted-review-cache-identity.ts 4 src/renderer/src/store/slices/hosted-review.ts -1 src/renderer/src/store/slices/preflight.ts -1 src/renderer/src/store/slices/project-group-owner-routing.ts +8 src/renderer/src/store/slices/jira-connection-actions.ts +4 src/renderer/src/store/slices/jira-read-coordination.ts +5 src/renderer/src/store/slices/linear/linear-slice-connection-actions.ts +3 src/renderer/src/store/slices/linear/linear-slice-scope.ts +1 src/renderer/src/store/slices/linear/linear-slice-status-actions.ts +2 src/renderer/src/store/slices/preflight.ts +2 src/renderer/src/store/slices/project-group-owner-routing.ts 1 src/renderer/src/store/slices/repo-host-identity.ts 1 src/renderer/src/store/slices/repo-reorder-host-split.ts +12 src/renderer/src/store/slices/repos.ts 1 src/renderer/src/store/slices/runtime-status.ts -3 src/renderer/src/store/slices/settings.ts +1 src/renderer/src/store/slices/settings-owner-hydration-publication.ts +5 src/renderer/src/store/slices/settings.ts +4 src/renderer/src/store/slices/store-test-helpers.ts 1 src/renderer/src/store/slices/worktree-visibility-owner-settings.ts 3 src/renderer/src/store/slices/worktree-visibility-settings-write.ts -1 src/renderer/src/store/slices/worktrees/create/create-worktree.ts -1 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts +7 src/renderer/src/store/slices/worktrees.ts +2 src/renderer/src/store/slices/worktrees/create/create-worktree.ts +2 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts 2 src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts -1 src/renderer/src/store/slices/worktrees/listing/detected-worktree-refresh.ts -2 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts +6 src/renderer/src/store/slices/worktrees/listing/detected-worktree-refresh.ts +4 src/renderer/src/store/slices/worktrees/listing/fetch-all-worktrees.ts +2 src/renderer/src/store/slices/worktrees/listing/fetch-detected-worktrees.ts +5 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts +2 src/renderer/src/store/slices/worktrees/listing/known-ssh-worktree-fetch.ts 1 src/renderer/src/store/slices/worktrees/listing/worktree-host-ownership.ts -1 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts -2 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts -1 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts -1 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts -5 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts +2 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts +4 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts +2 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts +3 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts +7 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts 1 src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.ts +2 src/renderer/src/store/slices/worktrees/session/worktree-terminal-reference.ts 1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts 1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts 1 src/renderer/src/store/slices/worktrees/teardown/remove-worktree.ts +6 src/renderer/src/web/preload-api/web-github-api.ts +8 src/renderer/src/web/preload-api/web-github-routes.ts 2 src/renderer/src/web/preload-api/web-preference-normalization.ts -5 src/renderer/src/web/preload-api/web-preferences-store.ts -1 src/renderer/src/web/preload-api/web-settings-api.ts +15 src/renderer/src/web/preload-api/web-preferences-store.ts +6 src/renderer/src/web/preload-api/web-settings-api.ts +1 src/renderer/src/web/web-preload-api.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index b7ce9002eb8f..7803f2d90d76 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -8,9 +8,8 @@ import { pathToFileURL } from 'node:url' // instead of by the resource's own host. Per-file counts may only go down. // - Owner routing: the three focus-routing helpers, counted together so renaming one into another // never lowers the count. -// - Focus reads: every read of the setting, including helpers that read it for the caller -// (`defaultCreationHost`, `getSettingsFocusedExecutionHostId`, …), so swapping one form for -// another never lowers it either. +// - Focus reads: every read of the setting plus every call of an exported function that reads it +// for the caller (discovered, not hand-listed), so swapping one form for another never lowers it. const SCAN_ROOT = 'src/renderer/src' const HELPER_NAMES = 'getActiveRuntimeTarget|legacyRouteFromSettings|settingsForRuntimeOwner' @@ -18,55 +17,101 @@ const HELPERS = `(?:${HELPER_NAMES})` const IMPORT_EXPORT_LIST = /\b(?:import|export)\s+(?:type\s+)?\{[^}]*\}/g // Calls and value uses (`.map(helper)`); definitions and type queries are not routing. const FOCUS_ROUTING_USE = new RegExp(`(?` are indexed types (`GlobalSettings['…']`), not reads. const SETTING_MEMBER_READ = /\??\.\s*activeRuntimeEnvironmentId\b|(?)\s*)\[\s*['"]activeRuntimeEnvironmentId['"]\s*\]/g -// `const { activeRuntimeEnvironmentId } = s` or `{ activeRuntimeEnvironmentId: id }: T = s`; -// object literals and `({ … }) =>` parameters are not reads of the setting. +// `const { activeRuntimeEnvironmentId } = s`, `{ activeRuntimeEnvironmentId: id }: T = s` or nested +// `{ settings: { activeRuntimeEnvironmentId } } = state`; object literals and `({ … }) =>` +// parameters are not reads of the setting. const SETTING_DESTRUCTURE_READ = - /\{[^{}]*\bactiveRuntimeEnvironmentId\b[^{}]*\}\s*(?::[^=;{}]*)?=(?![=>])/g + /\{[^{}]*\bactiveRuntimeEnvironmentId\b[^{}]*\}(?:\s*\})*\s*(?::[^=;{}]*)?=(?![=>])/g +// A top-level declaration: `[export] [async] function name` or `[export] const|let name =`. +const TOP_LEVEL_DECLARATION = + /^(export\s+)?(?:default\s+)?(?:async\s+)?(?:function\s*\*?\s*([\w$]+)|(?:const|let)\s+([\w$]+)\b)/gm +const TOP_LEVEL_BOUNDARY = + /^(?:export|import|function|async|const|let|type|interface|class|enum)\b/gm -/** - * Reads of the setting (member, element and destructuring reads) plus every helper that reads it - * on the caller's behalf. Object-literal keys are writes and are not counted. - */ -export function countFocusSettingReads(sourceText) { - const body = sourceText.replace(IMPORT_EXPORT_LIST, '') +function readerUsePattern(names) { + return names.size === 0 + ? null + : new RegExp( + `(? match.index) + return [...sourceText.matchAll(TOP_LEVEL_DECLARATION)].map((match) => { + const end = boundaries.find((index) => index > match.index) ?? sourceText.length + return { + name: match[2] ?? match[3], + exported: Boolean(match[1]), + text: sourceText.slice(match.index, end) + } + }) +} + +/** + * Functions that read the setting for their caller: the seeds plus every exported top-level + * function whose own body reads the setting or calls a seed. Hand lists miss look-alikes such as + * a copy of `getActiveRuntimeTarget` under another name; discovery does not. One level only: + * following calls transitively pulls in most of the renderer. + */ +export function discoverFocusReaders(sources) { + const readers = new Set(SEED_FOCUS_READERS) + const seedUse = readerUsePattern(new Set(SEED_FOCUS_READERS)) + for (const text of sources.values()) { + for (const { name, exported, text: body } of topLevelDeclarations( + text.replace(IMPORT_EXPORT_LIST, '') + )) { + seedUse.lastIndex = 0 + if (exported && (countSettingReads(body) > 0 || seedUse.test(body))) { + readers.add(name) + } + } + } + return readers +} + +/** + * Reads of the setting (member, element and destructuring reads) plus every call of a function + * that reads it for the caller. Object-literal keys are writes and are not counted. + */ +export function countFocusSettingReads(sourceText, readers = new Set(SEED_FOCUS_READERS)) { + const body = sourceText.replace(IMPORT_EXPORT_LIST, '') + const pattern = readerUsePattern(readers) + return countSettingReads(body) + (pattern ? (body.match(pattern)?.length ?? 0) : 0) +} + /** An alias hides later calls from the count, so it is refused outright. */ -export function hasFocusRoutingAlias(sourceText) { - return (sourceText.match(IMPORT_EXPORT_LIST) ?? []).some((list) => FOCUS_ROUTING_ALIAS.test(list)) +export function hasFocusRoutingAlias(sourceText, readers = new Set(SEED_FOCUS_READERS)) { + const alias = new RegExp(`\\b(?:${[...readers].join('|')})\\s+as\\b`) + return (sourceText.match(IMPORT_EXPORT_LIST) ?? []).some((list) => alias.test(list)) } export const RATCHETS = [ { name: 'owner-routing', baselinePath: 'config/owner-routing-baseline.txt', - count: countFocusRoutingCalls, + count: (text) => countFocusRoutingCalls(text), header: [ '# Renderer call sites that route by the Active Server focus setting:', '# getActiveRuntimeTarget( + legacyRouteFromSettings( + settingsForRuntimeOwner(, per file.', @@ -79,10 +124,10 @@ export const RATCHETS = [ { name: 'focus-setting-read', baselinePath: 'config/focus-setting-read-baseline.txt', - count: countFocusSettingReads, + count: (text, _rel, readers) => countFocusSettingReads(text, readers), header: [ '# Renderer reads of the Active Server setting (member, element and destructuring reads of', - '# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file.', + '# activeRuntimeEnvironmentId) plus calls of exported functions that read it, per file.', '# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read', '# the default host, through defaultCreationHost. Everything else routes by the owner.', '# Prune after removing reads: pnpm check:owner-routing-ratchet --prune' @@ -149,32 +194,45 @@ export function diffCounts(current, baseline) { return { grown: grown.sort(byFile), shrunk: shrunk.sort(byFile) } } -export function collectCurrentCounts(root = process.cwd(), count = countFocusRoutingCalls) { - const tracked = execFileSync('git', ['ls-files', SCAN_ROOT], { +function readTrackedSources(root, scanRoot) { + const tracked = execFileSync('git', ['ls-files', scanRoot], { cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }) .split('\n') .filter((rel) => rel && isScannedPath(rel)) - const counts = new Map() - const aliased = [] + const sources = new Map() for (const rel of tracked) { - let source try { - source = fs.readFileSync(path.join(root, rel), 'utf8') + sources.set(rel, fs.readFileSync(path.join(root, rel), 'utf8')) } catch { + // Deleted in the working tree but still tracked. + } + } + return sources +} + +export function collectCurrentCounts(root = process.cwd(), count = RATCHETS[0].count) { + const sources = new Map( + FOCUS_SCAN_ROOTS.flatMap((scanRoot) => [...readTrackedSources(root, scanRoot)]) + ) + const readers = discoverFocusReaders(sources) + const counts = new Map() + const aliased = [] + for (const [rel, source] of sources) { + if (!rel.startsWith(`${SCAN_ROOT}/`)) { continue } - const found = count(source) + const found = count(source, rel, readers) if (found > 0) { counts.set(rel, found) } - if (hasFocusRoutingAlias(source)) { + if (hasFocusRoutingAlias(source, readers)) { aliased.push(rel) } } - return { counts, aliased } + return { counts, aliased, readers } } function total(counts) { diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index dc80801fabc4..930517d2b080 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -4,6 +4,7 @@ import { countFocusRoutingCalls, countFocusSettingReads, diffCounts, + discoverFocusReaders, formatBaseline, hasFocusRoutingAlias, isScannedPath, @@ -55,17 +56,51 @@ describe('countFocusSettingReads', () => { expect(countFocusSettingReads(src)).toBe(5) }) - it('counts a shared focus helper, so wrapping one in an owner transport lowers nothing', () => { + it('discovers look-alike readers, so swapping a helper for one lowers nothing', () => { + const readers = discoverFocusReaders( + new Map([ + [ + 'src/shared/execution-host.ts', + 'export function getSettingsFocusedExecutionHostId(settings) {\n return settings?.activeRuntimeEnvironmentId\n}\n' + ], + [ + 'src/renderer/src/a.ts', + [ + 'export function getAutomationListTarget(settings) {', + ' const id = settings?.activeRuntimeEnvironmentId?.trim()', + " return id ? { kind: 'environment', environmentId: id } : { kind: 'local' }", + '}', + 'export const cacheKey = (s) => `k:${getActiveRuntimeTarget(s).kind}`', + 'function localOnly(s) {', + ' return s.activeRuntimeEnvironmentId', + '}', + 'export function ownerTarget(row) {', + ' return row.owner', + '}' + ].join('\n') + ] + ]) + ) + expect(readers.has('getAutomationListTarget')).toBe(true) + expect(readers.has('getSettingsFocusedExecutionHostId')).toBe(true) + expect(readers.has('cacheKey')).toBe(true) + expect(readers.has('localOnly')).toBe(false) + expect(readers.has('ownerTarget')).toBe(false) + expect(countFocusSettingReads('const t = getAutomationListTarget(settings)', readers)).toBe(1) expect( countFocusSettingReads( - 'const t = runtimeTargetForOwnerHostId(getSettingsFocusedExecutionHostId(s))' + 'const t = runtimeTargetForOwnerHostId(getSettingsFocusedExecutionHostId(s))', + readers ) ).toBe(1) - expect(countFocusSettingReads('const id = getSingleFocusedRuntimeEnvironmentId(state)')).toBe(1) + expect(countFocusSettingReads('export const cacheKey = (s) => s', readers)).toBe(0) }) it('counts destructuring reads', () => { expect(countFocusSettingReads('const { activeRuntimeEnvironmentId } = s')).toBe(1) + expect( + countFocusSettingReads('const { settings: { activeRuntimeEnvironmentId } } = state') + ).toBe(1) expect( countFocusSettingReads( "const { theme, activeRuntimeEnvironmentId: id }: Pick = s" @@ -96,7 +131,10 @@ describe('hasFocusRoutingAlias', () => { ).toBe(true) expect(hasFocusRoutingAlias("import { defaultCreationHost as host } from './d'")).toBe(true) expect( - hasFocusRoutingAlias("import { getSettingsFocusedExecutionHostId as h } from './e'") + hasFocusRoutingAlias( + "import { getAutomationListTarget as h } from './e'", + new Set(['getAutomationListTarget']) + ) ).toBe(true) expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget } from './rpc'")).toBe(false) }) From 704ecc264c17f3e34fd610cde3fc65c9dabf4956 Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 10:01:51 -0700 Subject: [PATCH 5/7] fix(lint): readers are found through imports and returned values Readers are keyed by defining file and resolved through imports and re-exports, so a same-named function elsewhere is not mistaken for one, and a function that returns a reader's result (a wrapper of a wrapper) is found too. Components are not readers, and a declaration ends at the next top-level statement. --- config/focus-setting-read-baseline.txt | 429 +++++++++++------- .../scripts/check-owner-routing-ratchet.mjs | 232 ++++++++-- .../check-owner-routing-ratchet.test.mjs | 59 ++- 3 files changed, 511 insertions(+), 209 deletions(-) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 7f4e6728acaa..f9c5f81a8219 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -3,23 +3,36 @@ # This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read # the default host, through defaultCreationHost. Everything else routes by the owner. # Prune after removing reads: pnpm check:owner-routing-ratchet --prune -2 src/renderer/src/app-shell/AppBackgroundServices.tsx +2 src/renderer/src/App.tsx +3 src/renderer/src/app-shell/use-app-shell-services.ts +1 src/renderer/src/app-shell/use-visible-review-refresh-reporting.ts 1 src/renderer/src/components/Landing.tsx -3 src/renderer/src/components/LinearIssueWorkspace.tsx +1 src/renderer/src/components/LinearIssueWorkspace.tsx +1 src/renderer/src/components/NativeChatResumeOnRestartGroups.tsx 1 src/renderer/src/components/NativeChatResumeOnRestartModal.tsx -3 src/renderer/src/components/NewWorkspaceComposerModal.tsx 2 src/renderer/src/components/QuickOpen.tsx +1 src/renderer/src/components/activity/ActivityPrototypePage.tsx +1 src/renderer/src/components/activity/activity-scope-filter-controls.tsx 3 src/renderer/src/components/activity/activity-thread-actions.ts 1 src/renderer/src/components/activity/use-agent-pane-threads.ts 4 src/renderer/src/components/agent/AgentSettingsDialog.tsx 1 src/renderer/src/components/automations/automation-host-client.ts +1 src/renderer/src/components/automations/automation-orca-save.ts +1 src/renderer/src/components/automations/automations-page-fixtures.ts +1 src/renderer/src/components/automations/use-automation-source-availability.ts 1 src/renderer/src/components/automations/use-automation-source-host-availability.ts -1 src/renderer/src/components/automations/use-automations-page-controller.ts +2 src/renderer/src/components/automations/use-automations-page-controller.ts 1 src/renderer/src/components/automations/use-automations-page-destination-form.ts 2 src/renderer/src/components/automations/use-automations-page-setup-state.ts +1 src/renderer/src/components/browser-pane/ReopenBrowserPageOnServerButton.tsx +1 src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.tsx 1 src/renderer/src/components/browser-pane/browser-reopen-on-server.ts +1 src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx +1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts 1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-stream.ts 1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-stream-activation.ts +2 src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.ts +1 src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.tsx 2 src/renderer/src/components/cmd-j/quick-action-context.ts 1 src/renderer/src/components/editor/EditorAutosaveController.tsx 2 src/renderer/src/components/editor/EditorContent.tsx @@ -27,66 +40,67 @@ 1 src/renderer/src/components/editor/EditorEditFileSurface.tsx 1 src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx 2 src/renderer/src/components/editor/EditorPanel.tsx -6 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx -11 src/renderer/src/components/editor/ExternalFileChangeCompareDialog.tsx +4 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx +3 src/renderer/src/components/editor/ExternalFileChangeCompareDialog.tsx +1 src/renderer/src/components/editor/MarkdownPreview.tsx 1 src/renderer/src/components/editor/MonacoGutterContextMenu.tsx +4 src/renderer/src/components/editor/RichMarkdownEditor.tsx +1 src/renderer/src/components/editor/combined-diff/CombinedDiffViewer.tsx 4 src/renderer/src/components/editor/combined-diff/load-sections/fetch-combined-diff-section.ts 1 src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-loader.ts 2 src/renderer/src/components/editor/csv/CsvPagedViewer.tsx 1 src/renderer/src/components/editor/csv/csv-file-content.ts 1 src/renderer/src/components/editor/csv/csv-link-routing.ts -6 src/renderer/src/components/editor/editor-dropped-file-open.ts +2 src/renderer/src/components/editor/editor-autosave-controller-test-fixture.ts +7 src/renderer/src/components/editor/editor-dropped-file-open.ts +1 src/renderer/src/components/editor/editor-header-file-rename.ts +1 src/renderer/src/components/editor/editor-model-lifetime-fixture.ts 6 src/renderer/src/components/editor/editor-restored-tab-conflict-scan.ts 1 src/renderer/src/components/editor/editor-save-queue.ts 1 src/renderer/src/components/editor/local-image-src-reader.ts 3 src/renderer/src/components/editor/markdown-document-list-request.ts 2 src/renderer/src/components/editor/markdown-preview-link-actions.ts -1 src/renderer/src/components/editor/markdown-preview-links.ts 1 src/renderer/src/components/editor/markdown-preview-local-images.ts 1 src/renderer/src/components/editor/markdown-preview-system-link-action.ts 1 src/renderer/src/components/editor/restored-editor-workspace-runtime-owner.ts 1 src/renderer/src/components/editor/rich-markdown-editor-config.ts 2 src/renderer/src/components/editor/rich-markdown-image-context.ts -3 src/renderer/src/components/editor/rich-markdown-image-insert.ts +4 src/renderer/src/components/editor/rich-markdown-image-insert.ts +1 src/renderer/src/components/editor/rich-markdown-paste-handler.ts 3 src/renderer/src/components/editor/rich-markdown-paste-image.ts 1 src/renderer/src/components/editor/use-markdown-preview-components.tsx 1 src/renderer/src/components/editor/use-markdown-preview-foundation.ts 1 src/renderer/src/components/editor/use-markdown-preview-source-foundation.ts -2 src/renderer/src/components/editor/useEditorPanelActiveTabContentLoad.ts -2 src/renderer/src/components/editor/useEditorPanelContentReloadTriggers.ts 4 src/renderer/src/components/editor/useEditorPanelContentState.ts 5 src/renderer/src/components/editor/useEditorPanelDiffContentLoader.ts -6 src/renderer/src/components/editor/useEditorPanelFileContentLoader.ts +7 src/renderer/src/components/editor/useEditorPanelFileContentLoader.ts 1 src/renderer/src/components/editor/useLocalImagePick.ts -4 src/renderer/src/components/editor/useLocalImageSrc.ts +5 src/renderer/src/components/editor/useLocalImageSrc.ts 5 src/renderer/src/components/editor/useMarkdownDocuments.ts 1 src/renderer/src/components/editor/useRichMarkdownProgrammaticSync.ts 1 src/renderer/src/components/editor/useRichMarkdownReconcileRoundTrip.ts +2 src/renderer/src/components/feature-wall/ConnectIntegrationsList.tsx 1 src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx 1 src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx +1 src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx 2 src/renderer/src/components/feature-wall/use-feature-wall-task-source-presentation.ts 2 src/renderer/src/components/feature-wall/use-integration-connection-status.ts -3 src/renderer/src/components/floating-terminal/floating-terminal-panel-test-harness.ts -3 src/renderer/src/components/floating-terminal/floating-terminal-panel-test-module-mocks.ts +1 src/renderer/src/components/floating-terminal/use-floating-terminal-panel-controller.ts 1 src/renderer/src/components/floating-terminal/use-floating-terminal-panel-store-state.ts -2 src/renderer/src/components/github-item-dialog/discuss-item/conversation-tab-pr-sidebar.tsx 1 src/renderer/src/components/github-item-dialog/discuss-item/conversation-tab.tsx 4 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts -4 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx +7 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx 2 src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx 5 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-panel.tsx 6 src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-request-actions.ts -1 src/renderer/src/components/github-project/ProjectCellIdentity.tsx 1 src/renderer/src/components/github-project/ProjectCellIssueType.tsx 2 src/renderer/src/components/github-project/ProjectCellRepositoryEditors.tsx -2 src/renderer/src/components/github-project/ProjectItemSlugDialog.tsx 2 src/renderer/src/components/github-project/ProjectPicker.tsx -3 src/renderer/src/components/github-project/ProjectViewWrapper.tsx 4 src/renderer/src/components/github-project/project-picker-runtime.ts 1 src/renderer/src/components/github-project/slug-dialog/AssigneesEditor.tsx 1 src/renderer/src/components/github-project/slug-dialog/Comments.tsx 1 src/renderer/src/components/github-project/slug-dialog/LabelsEditor.tsx -9 src/renderer/src/components/github-project/slug-dialog/SlugDialogBody.tsx +1 src/renderer/src/components/github-project/slug-dialog/SlugDialogBody.tsx 2 src/renderer/src/components/github-project/useProjectPickerBrowse.ts 2 src/renderer/src/components/github-project/useProjectViewTable.ts 3 src/renderer/src/components/github/PRAssigneesPanel.tsx @@ -97,68 +111,79 @@ 1 src/renderer/src/components/jira-connect-dialog.tsx 1 src/renderer/src/components/landing-preflight-runtime.ts 1 src/renderer/src/components/linear-api-key-dialog.tsx -5 src/renderer/src/components/linear-issue-activity.tsx -2 src/renderer/src/components/linear-issue-clipboard.ts -9 src/renderer/src/components/linear-issue-project-selector.tsx -7 src/renderer/src/components/linear-issue-sub-issues.tsx -14 src/renderer/src/components/linear-issue-workspace-header.tsx -8 src/renderer/src/components/linear-issue-workspace-sidebar.tsx +2 src/renderer/src/components/native-chat-resume-modal.test-support.tsx +1 src/renderer/src/components/native-chat-resume-tree.test-support.ts +2 src/renderer/src/components/native-chat/NativeChatComposer.tsx +1 src/renderer/src/components/native-chat/NativeChatResolvedView.tsx 1 src/renderer/src/components/native-chat/NativeChatTranscriptChrome.tsx 1 src/renderer/src/components/native-chat/NativeChatVisualFrame.tsx 1 src/renderer/src/components/native-chat/claude-model-switch-confirmation.ts 4 src/renderer/src/components/native-chat/native-chat-file-link-existence.ts +1 src/renderer/src/components/native-chat/native-chat-file-link.ts +1 src/renderer/src/components/native-chat/native-chat-http-link-source-owner.ts 1 src/renderer/src/components/native-chat/native-chat-observed-send.ts -4 src/renderer/src/components/native-chat/native-chat-runtime-send.ts +2 src/renderer/src/components/native-chat/native-chat-runtime-image-send.ts +2 src/renderer/src/components/native-chat/native-chat-runtime-owner.ts +6 src/renderer/src/components/native-chat/native-chat-runtime-send.ts 2 src/renderer/src/components/native-chat/native-chat-session-option-discovery.ts -1 src/renderer/src/components/native-chat/use-native-chat-file-link-click.ts +2 src/renderer/src/components/native-chat/use-native-chat-file-link-click.ts 1 src/renderer/src/components/native-chat/use-native-chat-file-link-existence.tsx -1 src/renderer/src/components/native-chat/use-native-chat-interactive-send.ts +6 src/renderer/src/components/native-chat/use-native-chat-interactive-send.ts 1 src/renderer/src/components/native-chat/use-native-chat-mention-files.ts +2 src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.ts +4 src/renderer/src/components/native-chat/use-native-chat-pty-composer-send.ts +2 src/renderer/src/components/native-chat/use-native-chat-session-option-command.ts 1 src/renderer/src/components/new-workspace/smart-workspace-repo-slug.ts 1 src/renderer/src/components/new-workspace/use-jira-source-connection.ts 2 src/renderer/src/components/new-workspace/use-linear-source-connection.ts 1 src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts 2 src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts -1 src/renderer/src/components/onboarding/IntegrationsStep.tsx +1 src/renderer/src/components/new-workspace/use-work-item-source-search.ts 1 src/renderer/src/components/ports/WorkspacePortScanner.tsx 1 src/renderer/src/components/pull-request-page/actions/merge-actions.ts 1 src/renderer/src/components/pull-request-page/actions/panel.tsx -4 src/renderer/src/components/pull-request-page/conversation/tab.tsx +2 src/renderer/src/components/pull-request-page/conversation/tab.tsx 3 src/renderer/src/components/pull-request-page/edit/issue-updates.ts -4 src/renderer/src/components/pull-request-page/edit/section.tsx +8 src/renderer/src/components/pull-request-page/edit/section.tsx 4 src/renderer/src/components/pull-request-page/reviewers/panel.tsx -6 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts +2 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts 2 src/renderer/src/components/quick-open-file-list-test-harness.ts -5 src/renderer/src/components/quick-open-file-list.ts -2 src/renderer/src/components/quick-open-file-navigation.ts +6 src/renderer/src/components/quick-open-file-list.ts +4 src/renderer/src/components/quick-open-file-navigation.ts 2 src/renderer/src/components/quick-open-recent-validation.ts 3 src/renderer/src/components/repo/CreateFromPicker.tsx -1 src/renderer/src/components/right-sidebar/FileExplorer.tsx +1 src/renderer/src/components/right-sidebar/ChecksPanel.tsx +3 src/renderer/src/components/right-sidebar/FileExplorer.tsx +1 src/renderer/src/components/right-sidebar/FileExplorerFilesTreePane.tsx 1 src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.tsx 1 src/renderer/src/components/right-sidebar/ai-vault-original-pane-actions.ts 1 src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts 3 src/renderer/src/components/right-sidebar/checks-panel-hosted-review-click-routing.ts -1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-check-and-review-actions.tsx +1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-controller-state.tsx 1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-create-review.tsx 2 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-generation.tsx 3 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-git-status-effects.tsx 3 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-manual-refresh.tsx 1 src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-review-state.tsx 2 src/renderer/src/components/right-sidebar/file-explorer-deletion-editors.ts -2 src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +1 src/renderer/src/components/right-sidebar/file-explorer-directory-listing.ts +4 src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts +1 src/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsx +1 src/renderer/src/components/right-sidebar/git-status-file-watch-refresh.ts 5 src/renderer/src/components/right-sidebar/git-status-refresh.ts -1 src/renderer/src/components/right-sidebar/local-workspace-ports-panel.tsx 1 src/renderer/src/components/right-sidebar/push-target-upstream-refresh-cache.ts -1 src/renderer/src/components/right-sidebar/source-control/ai/use-ai.ts +2 src/renderer/src/components/right-sidebar/source-control/ai/use-ai.ts 5 src/renderer/src/components/right-sidebar/source-control/commit/use-bulk-actions.ts 1 src/renderer/src/components/right-sidebar/source-control/commit/use-commit-action.ts 2 src/renderer/src/components/right-sidebar/source-control/commit/use-commit-message-generation.ts 1 src/renderer/src/components/right-sidebar/source-control/commit/use-discard-confirmation.ts 7 src/renderer/src/components/right-sidebar/source-control/commit/use-entry-mutations.ts +1 src/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsx 2 src/renderer/src/components/right-sidebar/source-control/listing/use-file-listing.ts 2 src/renderer/src/components/right-sidebar/source-control/listing/use-submodule-status.ts 1 src/renderer/src/components/right-sidebar/source-control/listing/use-worktree-context.ts -1 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-model.ts +1 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-foundation.ts +2 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-model.ts 2 src/renderer/src/components/right-sidebar/source-control/panel/use-panel-state.ts 1 src/renderer/src/components/right-sidebar/source-control/review/create-pr-intent-run-snapshot.ts 1 src/renderer/src/components/right-sidebar/source-control/review/use-create-pr-intent-commit-message.ts @@ -178,84 +203,93 @@ 2 src/renderer/src/components/right-sidebar/use-create-pull-request-field-seeding.ts 1 src/renderer/src/components/right-sidebar/use-file-explorer-ignored-paths.ts 1 src/renderer/src/components/right-sidebar/use-file-explorer-name-filter.ts -2 src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts +2 src/renderer/src/components/right-sidebar/use-file-explorer-node-commands.ts +6 src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts 3 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts -2 src/renderer/src/components/right-sidebar/useFileDeletion.ts +3 src/renderer/src/components/right-sidebar/useFileDeletion.ts +1 src/renderer/src/components/right-sidebar/useFileDuplicate.ts +2 src/renderer/src/components/right-sidebar/useFileExplorerDragDrop.ts 2 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts -1 src/renderer/src/components/right-sidebar/useFileExplorerImport.ts -1 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts -1 src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts -3 src/renderer/src/components/right-sidebar/useFileSearchRunner.ts -1 src/renderer/src/components/right-sidebar/useGitStatusPolling.ts +4 src/renderer/src/components/right-sidebar/useFileExplorerImport.ts +3 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts +1 src/renderer/src/components/right-sidebar/useFileExplorerMoveDrop.ts +2 src/renderer/src/components/right-sidebar/useFileExplorerTree.ts +2 src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts +4 src/renderer/src/components/right-sidebar/useFileSearchPanel.ts +4 src/renderer/src/components/right-sidebar/useFileSearchRunner.ts +1 src/renderer/src/components/right-sidebar/useFileSearchScope.ts +2 src/renderer/src/components/right-sidebar/useGitStatusPolling.ts 3 src/renderer/src/components/settings/BaseRefPicker.tsx 2 src/renderer/src/components/settings/BrowserPane.tsx 1 src/renderer/src/components/settings/CliSkillRuntimeSetup.tsx 1 src/renderer/src/components/settings/CommitMessageAiPane.tsx -16 src/renderer/src/components/settings/GeneralPane.tsx -2 src/renderer/src/components/settings/LinearAgentSkillPane.tsx +1 src/renderer/src/components/settings/GeneralPane.tsx +1 src/renderer/src/components/settings/IntegrationsPane.tsx +3 src/renderer/src/components/settings/LinearAgentSkillPane.tsx +1 src/renderer/src/components/settings/McpConfigSection.tsx 1 src/renderer/src/components/settings/RepositoryForkSyncSection.tsx -20 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx -4 src/renderer/src/components/settings/RepositoryIconColorSection.tsx -3 src/renderer/src/components/settings/RepositoryIconEmojiPicker.tsx -5 src/renderer/src/components/settings/RepositoryIconPicker.tsx -13 src/renderer/src/components/settings/RepositoryIconTabs.tsx -4 src/renderer/src/components/settings/RepositoryPane.tsx +2 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx +1 src/renderer/src/components/settings/RepositoryHooksSection.tsx +1 src/renderer/src/components/settings/RepositoryIconPicker.tsx 4 src/renderer/src/components/settings/RuntimeEnvironmentsPane.tsx 1 src/renderer/src/components/settings/Settings.tsx +1 src/renderer/src/components/settings/SettingsSetupGuidePane.tsx 1 src/renderer/src/components/settings/ShortcutsPane.tsx -1 src/renderer/src/components/settings/TaskSourceLinearSetup.tsx +2 src/renderer/src/components/settings/TasksPane.tsx +1 src/renderer/src/components/settings/WorkspaceDirectorySetting.tsx 1 src/renderer/src/components/settings/bitbucket-credentials-dialog.tsx -1 src/renderer/src/components/settings/cli-source-control-integration-cards.tsx -3 src/renderer/src/components/settings/general-search.ts +1 src/renderer/src/components/settings/bitbucket-integration-card.tsx +3 src/renderer/src/components/settings/cli-source-control-integration-cards.tsx 3 src/renderer/src/components/settings/jira-integration-card.tsx 1 src/renderer/src/components/settings/linear-agent-skill-install-cta.tsx 1 src/renderer/src/components/settings/mcp-config-inspection.ts 2 src/renderer/src/components/settings/provider-account-scope.ts -9 src/renderer/src/components/settings/runtime-active-server-section.tsx -11 src/renderer/src/components/settings/runtime-environment-dialogs.tsx -22 src/renderer/src/components/settings/runtime-environment-host-details.ts -8 src/renderer/src/components/settings/runtime-server-row.tsx -20 src/renderer/src/components/settings/runtime-server-workflow-sections.tsx -6 src/renderer/src/components/settings/runtime-servers-connect-section.tsx -1 src/renderer/src/components/settings/settings-capability-section-renderers.tsx -2 src/renderer/src/components/settings/settings-remote-security-section-renderers.tsx -2 src/renderer/src/components/settings/settings-setup-workflow-section-renderers.tsx +1 src/renderer/src/components/settings/settings-persistence-test-fixture.ts +2 src/renderer/src/components/settings/settings-setup-guide-progress.ts 1 src/renderer/src/components/settings/source-control-preflight-card-status.ts -4 src/renderer/src/components/settings/task-tracker-integration-cards.tsx +2 src/renderer/src/components/settings/task-tracker-integration-cards.tsx +2 src/renderer/src/components/settings/token-source-control-integration-cards.tsx 2 src/renderer/src/components/settings/use-integration-provider-status-refresh.ts 3 src/renderer/src/components/settings/use-repository-issue-command.ts -1 src/renderer/src/components/settings/use-runtime-environment-catalog.ts -3 src/renderer/src/components/settings/use-runtime-environment-connection-actions.ts -7 src/renderer/src/components/settings/use-runtime-environment-mutation-actions.ts +1 src/renderer/src/components/settings/use-runtime-environment-mutation-actions.ts 2 src/renderer/src/components/settings/use-settings-navigation-model.ts -2 src/renderer/src/components/settings/use-settings-store-model.ts -2 src/renderer/src/components/settings/use-task-source-provider-readiness.ts +3 src/renderer/src/components/settings/use-settings-store-model.ts +2 src/renderer/src/components/settings/use-settings-terminal-model.ts +4 src/renderer/src/components/settings/use-task-source-provider-readiness.ts +1 src/renderer/src/components/setup-guide/SetupGuideModal.tsx +1 src/renderer/src/components/setup-guide/SetupGuideTelemetryObserver.tsx 1 src/renderer/src/components/setup-guide/setup-guide-progress-readiness.ts 3 src/renderer/src/components/setup-guide/use-setup-guide-progress.ts -11 src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx -10 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +1 src/renderer/src/components/sidebar/DeleteWorktreeDialog.tsx +3 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx +1 src/renderer/src/components/sidebar/SavedHostScopeWidenGate.tsx +1 src/renderer/src/components/sidebar/SetupGuideSidebarEntry.tsx +1 src/renderer/src/components/sidebar/SidebarAgentsList.tsx +1 src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.tsx 1 src/renderer/src/components/sidebar/SidebarTaskNavButton.tsx 1 src/renderer/src/components/sidebar/WorktreeCardAgents.tsx -1 src/renderer/src/components/sidebar/WorktreeList.tsx +2 src/renderer/src/components/sidebar/WorktreeList.tsx +1 src/renderer/src/components/sidebar/WorktreeRowOpenInSubMenu.tsx 1 src/renderer/src/components/sidebar/add-repo-runtime-owner.ts -3 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts -8 src/renderer/src/components/sidebar/linear-agent-skill-setup-copy.ts -1 src/renderer/src/components/sidebar/linear-agent-skill-setup-reminder-toast.ts +1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts 2 src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts 2 src/renderer/src/components/sidebar/sidebar-host-options.ts 1 src/renderer/src/components/sidebar/sidebar-project-drop.ts -1 src/renderer/src/components/sidebar/use-add-repo-host-selection.ts +2 src/renderer/src/components/sidebar/use-add-repo-host-selection.ts 1 src/renderer/src/components/sidebar/use-delete-worktree-status-hydration.ts 1 src/renderer/src/components/sidebar/use-sidebar-host-scope-options.ts 1 src/renderer/src/components/sidebar/use-visible-workspace-kanban-worktree-ids.ts +1 src/renderer/src/components/sidebar/use-workspace-kanban-board-projection.ts +1 src/renderer/src/components/sidebar/use-workspace-reference-details.ts 1 src/renderer/src/components/sidebar/useAddRepoCloneFlow.ts 1 src/renderer/src/components/sidebar/useCreateRepo.ts 2 src/renderer/src/components/sidebar/useSidebarProjectDrop.ts 1 src/renderer/src/components/sidebar/visible-worktree-options-from-state.ts 2 src/renderer/src/components/sidebar/visible-worktrees.ts -1 src/renderer/src/components/sidebar/workspace-reference-details.ts -1 src/renderer/src/components/sidebar/worktree-card-parent-content.tsx -3 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx +1 src/renderer/src/components/sidebar/workspace-options-menu-items.tsx +2 src/renderer/src/components/sidebar/workspace-reference-details.ts +1 src/renderer/src/components/sidebar/workspace-reference-refresh-targets.ts +1 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx 1 src/renderer/src/components/sidebar/worktree-filter-visibility.ts 2 src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts 2 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts @@ -264,66 +298,77 @@ 1 src/renderer/src/components/skills/SkillsPage.tsx 7 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx 1 src/renderer/src/components/status-bar/CodexSwitcherMenu.tsx -9 src/renderer/src/components/status-bar/RuntimeHostStatusRow.tsx -5 src/renderer/src/components/status-bar/SshStatusSegment.tsx -3 src/renderer/src/components/status-bar/SshTargetStatusRow.tsx -1 src/renderer/src/components/status-bar/StatusBar.tsx -5 src/renderer/src/components/status-bar/StatusBarSurface.tsx +1 src/renderer/src/components/status-bar/SshStatusSegment.tsx +1 src/renderer/src/components/status-bar/StatusBarSurface.tsx 2 src/renderer/src/components/status-bar/provider-account-sync-key.ts -2 src/renderer/src/components/status-bar/remote-host-connection-status.ts -1 src/renderer/src/components/status-bar/runtime-environment-explicit-connect.ts -5 src/renderer/src/components/status-bar/ssh-status-segment-copy.ts 1 src/renderer/src/components/status-bar/status-bar-claude-accounts.ts 1 src/renderer/src/components/status-bar/status-bar-codex-accounts.ts 1 src/renderer/src/components/status-bar/usage-provider-settings-target.ts 7 src/renderer/src/components/status-bar/use-codex-switcher-controller.ts 1 src/renderer/src/components/status-bar/use-status-bar-controller.ts 1 src/renderer/src/components/status-bar/use-workspace-space-git-refresh-action.ts +1 src/renderer/src/components/status-bar/use-workspace-space-manager-panel.ts +1 src/renderer/src/components/tab-bar/EditorFileTab.tsx 1 src/renderer/src/components/tab-bar/TabBar.tsx -1 src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx -4 src/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-test-harness.ts -1 src/renderer/src/components/tab-bar/tab-create-entry-action.ts -2 src/renderer/src/components/tab-bar/tab-create-entry-local-path.ts -2 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts -3 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts -1 src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts +2 src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx +3 src/renderer/src/components/tab-bar/tab-bar-windows-shell-launch-test-harness.ts +2 src/renderer/src/components/tab-bar/tab-create-entry-action.ts +3 src/renderer/src/components/tab-bar/tab-create-entry-local-path.ts +1 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts +4 src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts +2 src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts 1 src/renderer/src/components/tab-bar/windows-shell-menu-visibility.ts 1 src/renderer/src/components/tab-group/AiVaultSessionDropLayer.tsx -1 src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts -2 src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts -6 src/renderer/src/components/task-page-github-reviewer-actions.ts -1 src/renderer/src/components/task-page/TaskPage.tsx +3 src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts +5 src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts +2 src/renderer/src/components/task-page-github-reviewer-actions.ts +2 src/renderer/src/components/task-page/TaskPage.tsx 2 src/renderer/src/components/task-page/github/AssigneesCell.tsx 2 src/renderer/src/components/task-page/github/MergeCell.tsx 2 src/renderer/src/components/task-page/github/ReviewCell.tsx -3 src/renderer/src/components/task-page/github/Rows.tsx 1 src/renderer/src/components/task-page/github/StatusCell.tsx -1 src/renderer/src/components/task-page/linear/ConnectDialog.tsx -3 src/renderer/src/components/task-page/linear/Content.tsx 2 src/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.ts 1 src/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.ts 1 src/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.ts -6 src/renderer/src/components/terminal-pane/terminal-file-link-actions.ts +1 src/renderer/src/components/terminal-pane/retire-unbound-runtime-terminal-pane.ts +2 src/renderer/src/components/terminal-pane/terminal-drop-runtime-owner.ts +8 src/renderer/src/components/terminal-pane/terminal-file-link-actions.ts 1 src/renderer/src/components/terminal-pane/terminal-file-link-hit-testing.ts -1 src/renderer/src/components/terminal-pane/terminal-file-link-target.ts -4 src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts +2 src/renderer/src/components/terminal-pane/terminal-file-link-target.ts +6 src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts 3 src/renderer/src/components/terminal-pane/terminal-file-path-mapping.ts 1 src/renderer/src/components/terminal-pane/terminal-fit-restore.ts -1 src/renderer/src/components/terminal-pane/terminal-host-workspace-file.ts +2 src/renderer/src/components/terminal-pane/terminal-host-workspace-file.ts +1 src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts 2 src/renderer/src/components/terminal-pane/terminal-link-handlers.ts 1 src/renderer/src/components/terminal-pane/terminal-link-open-hints.ts +1 src/renderer/src/components/terminal-pane/terminal-link-provider-buffer-fixtures.ts 1 src/renderer/src/components/terminal-pane/terminal-native-file-drop.ts -1 src/renderer/src/components/terminal-pane/terminal-osc-link-routing.ts +2 src/renderer/src/components/terminal-pane/terminal-pane-close-admission.ts +1 src/renderer/src/components/terminal-pane/terminal-pane-menu-paste.ts 1 src/renderer/src/components/terminal-pane/terminal-pane-mount-preparation.ts -2 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts +1 src/renderer/src/components/terminal-pane/terminal-pane-pane-links.ts +1 src/renderer/src/components/terminal-pane/terminal-pane-paste-execution.ts +1 src/renderer/src/components/terminal-pane/terminal-pane-paste-listeners.ts +3 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts 2 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts +1 src/renderer/src/components/terminal-pane/use-terminal-pane-close-actions.ts +1 src/renderer/src/components/terminal-pane/use-terminal-pane-controller.ts +1 src/renderer/src/components/terminal-pane/use-terminal-pane-mount-lifecycle.ts +1 src/renderer/src/components/terminal-pane/use-terminal-pane-paste-listeners.ts +1 src/renderer/src/components/terminal-workspace-model.ts 1 src/renderer/src/components/terminal/pty-running-work-probe.ts +1 src/renderer/src/components/terminal/running-terminal-close-guard.ts +1 src/renderer/src/components/terminal/terminal-tab-create.ts +1 src/renderer/src/components/terminal/window-close-running-work.ts 2 src/renderer/src/components/use-quick-open-interaction.ts 3 src/renderer/src/components/use-task-page-github-issue-draft.ts -1 src/renderer/src/components/use-task-page-source-availability.ts +2 src/renderer/src/components/use-task-page-source-availability.ts 2 src/renderer/src/components/use-task-page-store-bindings.ts -4 src/renderer/src/components/use-terminal-create-actions.ts -1 src/renderer/src/components/use-worktree-jump-palette-controller.ts +2 src/renderer/src/components/use-terminal-activation-actions.ts +2 src/renderer/src/components/use-terminal-bulk-close-actions.ts +8 src/renderer/src/components/use-terminal-create-actions.ts +2 src/renderer/src/components/use-worktree-jump-palette-controller.ts 2 src/renderer/src/components/use-worktree-jump-palette-filter.ts 3 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts 2 src/renderer/src/components/use-worktree-jump-palette-store-state.ts @@ -332,7 +377,7 @@ 4 src/renderer/src/hooks/composer-state/attachment-drop-state.ts 1 src/renderer/src/hooks/composer-state/composer-attachment-drop-state.ts 1 src/renderer/src/hooks/composer-state/composer-navigation-actions.ts -2 src/renderer/src/hooks/composer-state/composer-source-state.ts +3 src/renderer/src/hooks/composer-state/composer-source-state.ts 1 src/renderer/src/hooks/composer-state/composer-submit-orchestration.ts 2 src/renderer/src/hooks/composer-state/composer-target-state.ts 1 src/renderer/src/hooks/composer-state/github-provider-selection.ts @@ -342,78 +387,103 @@ 1 src/renderer/src/hooks/composer-state/quick-creation-execution.ts 1 src/renderer/src/hooks/composer-state/workspace-identity-state.ts 1 src/renderer/src/hooks/editor-external-watch-disk-verification.ts -1 src/renderer/src/hooks/editor-external-watch-targets.ts +2 src/renderer/src/hooks/editor-external-watch-targets.ts 1 src/renderer/src/hooks/editor-runtime-file-watch.ts -1 src/renderer/src/hooks/ipc-events-terminal-create-test-harness.ts -1 src/renderer/src/hooks/ipc-events-test-harness.ts 2 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts +1 src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts 1 src/renderer/src/hooks/ipc-events/settings-sidebar-ipc-bridge.ts -26 src/renderer/src/hooks/settings-navigation-capability-sections.ts -10 src/renderer/src/hooks/settings-navigation-interface-sections.ts -25 src/renderer/src/hooks/settings-navigation-remote-sections.ts -23 src/renderer/src/hooks/settings-navigation-workflow-sections.ts +2 src/renderer/src/hooks/ipc-events/tab-lifecycle-ipc-bridge.ts 3 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts 1 src/renderer/src/hooks/useActiveProjectSkillRuntime.ts -1 src/renderer/src/hooks/useEditorExternalWatch.ts +2 src/renderer/src/hooks/useEditorExternalWatch.ts 2 src/renderer/src/hooks/useGitHubRepoMetadata.ts 4 src/renderer/src/hooks/useGitHubSlugMetadata.ts 1 src/renderer/src/hooks/useInstalledAgentSkills.ts +1 src/renderer/src/hooks/useIpcEvents.ts 1 src/renderer/src/hooks/useIssueMetadata.ts 1 src/renderer/src/hooks/useLinearProviderConnected.ts -3 src/renderer/src/hooks/useSettingsNavigationMetadata.ts +7 src/renderer/src/hooks/useSettingsNavigationMetadata.ts +1 src/renderer/src/hooks/useUnreadDockBadge.ts 2 src/renderer/src/hooks/useWindowsTerminalCapabilityOwnerKey.ts -7 src/renderer/src/lib/activate-ai-vault-structured-session.ts +10 src/renderer/src/lib/activate-ai-vault-structured-session.ts 1 src/renderer/src/lib/active-agent-note-send.ts 1 src/renderer/src/lib/active-agent-note-target.ts +1 src/renderer/src/lib/agent-background-session-launch-host.ts 1 src/renderer/src/lib/agent-draft-paste-content.ts 1 src/renderer/src/lib/agent-draft-readiness.ts 2 src/renderer/src/lib/agent-followup-delivery.ts 1 src/renderer/src/lib/agent-hibernation-coordinator-test-fixture.ts -3 src/renderer/src/lib/agent-paste-draft.ts +3 src/renderer/src/lib/agent-hibernation-coordinator.ts +5 src/renderer/src/lib/agent-paste-draft.ts 1 src/renderer/src/lib/agent-ready-wait.ts 1 src/renderer/src/lib/automation-session-observer.ts 1 src/renderer/src/lib/client-creation-action-error.ts -2 src/renderer/src/lib/client-creation-action-policy.ts -4 src/renderer/src/lib/codex-pane-selection-lane.ts +3 src/renderer/src/lib/client-creation-action-policy.ts +3 src/renderer/src/lib/codex-pane-selection-lane.ts 2 src/renderer/src/lib/codex-session-restart.ts 1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host +1 src/renderer/src/lib/doc-preview-grants.ts 3 src/renderer/src/lib/ensure-hooks-confirmed.ts -2 src/renderer/src/lib/file-preview.ts +1 src/renderer/src/lib/execute-open-editor-path-move.ts +1 src/renderer/src/lib/execution-host-facts.ts +6 src/renderer/src/lib/file-preview.ts 1 src/renderer/src/lib/file-search-result-owner.ts 2 src/renderer/src/lib/folder-workspace-runtime-owner.ts 1 src/renderer/src/lib/github-pr-start-point.ts 1 src/renderer/src/lib/github-source-runtime-context.ts 2 src/renderer/src/lib/github-work-item-source-lookup.ts +1 src/renderer/src/lib/host-mirrored-pane-liveness.ts 1 src/renderer/src/lib/hosted-review-repo-read.ts 6 src/renderer/src/lib/http-link-destinations.ts 3 src/renderer/src/lib/http-link-routing.ts 2 src/renderer/src/lib/launch-agent-background-session.ts +1 src/renderer/src/lib/launch-agent-execution-context.ts +2 src/renderer/src/lib/launch-agent-in-new-tab.ts +1 src/renderer/src/lib/launch-agent-web-host-tab.ts +2 src/renderer/src/lib/launch-ai-vault-session.ts 1 src/renderer/src/lib/launch-work-item-direct-preflight.ts +2 src/renderer/src/lib/launch-work-item-direct.ts 1 src/renderer/src/lib/launch-worktree-background-terminals.ts 3 src/renderer/src/lib/local-file-access.ts 2 src/renderer/src/lib/local-preflight-context.ts 1 src/renderer/src/lib/markdown-document-templates.ts +1 src/renderer/src/lib/new-workspace.ts 1 src/renderer/src/lib/open-agent-message-sender.ts 2 src/renderer/src/lib/provider-runtime-context.ts -1 src/renderer/src/lib/remap-open-editor-tabs-for-path-change.ts +1 src/renderer/src/lib/remote-workspace-drop-test-fixtures.ts +2 src/renderer/src/lib/rename-file.ts 4 src/renderer/src/lib/repo-runtime-owner.ts -3 src/renderer/src/lib/repo-slug-cache.ts -5 src/renderer/src/lib/repo-slug-index.ts +2 src/renderer/src/lib/repo-slug-cache.ts +3 src/renderer/src/lib/repo-slug-index.ts 2 src/renderer/src/lib/resolve-owner.ts 1 src/renderer/src/lib/runtime-session-mirror-owners.ts 1 src/renderer/src/lib/runtime-session-mirror-targets.ts 1 src/renderer/src/lib/single-runtime-legacy-owner.ts -2 src/renderer/src/lib/structured-agent-session-tab-activation.ts -2 src/renderer/src/lib/terminal-worktree-route.ts +3 src/renderer/src/lib/structured-agent-session-tab-activation.ts +1 src/renderer/src/lib/tab-number-shortcuts.ts +1 src/renderer/src/lib/terminal-tab-owner-activation.ts +4 src/renderer/src/lib/terminal-worktree-route.ts 3 src/renderer/src/lib/unread-badge-count-selector.ts +1 src/renderer/src/lib/user-opened-local-path.ts +2 src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts +1 src/renderer/src/lib/workspace-browser-tab-close.ts 4 src/renderer/src/lib/workspace-browser-tab-open.ts 3 src/renderer/src/lib/workspace-file-host-routing.ts -1 src/renderer/src/lib/worktree-activation.ts +1 src/renderer/src/lib/workspace-tab-palette-activation.ts +1 src/renderer/src/lib/workspace-terminal-host-authority.ts +1 src/renderer/src/lib/worktree-activation-pty-inventory.ts +3 src/renderer/src/lib/worktree-activation.ts +1 src/renderer/src/lib/worktree-creation-agent-seeds.ts +1 src/renderer/src/lib/worktree-creation-flow-execute.ts 1 src/renderer/src/lib/worktree-creation-flow-startup.ts 1 src/renderer/src/lib/worktree-creation-flow.ts +1 src/renderer/src/lib/worktree-creation-hook-preparation.ts 1 src/renderer/src/lib/worktree-operation-route.ts -1 src/renderer/src/lib/worktree-runtime-owner.ts +1 src/renderer/src/lib/worktree-row-open-in-owner.ts +3 src/renderer/src/lib/worktree-runtime-owner.ts +1 src/renderer/src/lib/worktree-sort-order-host-split.ts +1 src/renderer/src/runtime/close-mirrored-editor-tab.ts 3 src/renderer/src/runtime/mobile-markdown-bridge.ts 1 src/renderer/src/runtime/runtime-client-target.ts 2 src/renderer/src/runtime/runtime-file-import-client.ts @@ -446,28 +516,34 @@ 1 src/renderer/src/runtime/runtime-terminal-verified-input.ts 5 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts 2 src/renderer/src/runtime/web-runtime-browser-creation.ts +1 src/renderer/src/runtime/web-runtime-initial-terminal-bootstrap-dispatch.ts 1 src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts 1 src/renderer/src/runtime/web-runtime-session-tab-move.ts +2 src/renderer/src/runtime/web-runtime-terminal-actions.ts 1 src/renderer/src/runtime/web-runtime-terminal-create-operation.ts 3 src/renderer/src/runtime/web-runtime-terminal-creation.ts 1 src/renderer/src/runtime/web-runtime-worktree-activation.ts +1 src/renderer/src/runtime/web-session-tabs-sync/active-session-subscription.ts 2 src/renderer/src/runtime/web-session-tabs-sync/global-session-subscription.ts 1 src/renderer/src/runtime/web-session-tabs-sync/tracking-decisions.ts 2 src/renderer/src/runtime/web-session-tabs-sync/use-web-session-tabs-sync.ts 2 src/renderer/src/store/folder-workspaces/folder-path-status-actions.ts 2 src/renderer/src/store/folder-workspaces/folder-workspace-catalog-actions.ts -3 src/renderer/src/store/folder-workspaces/folder-workspace-mutations.ts +5 src/renderer/src/store/folder-workspaces/folder-workspace-mutations.ts 1 src/renderer/src/store/folder-workspaces/folder-workspace-routing.ts 1 src/renderer/src/store/github/cache-identity.ts 3 src/renderer/src/store/github/project-actions.ts 2 src/renderer/src/store/github/project-field-mutations.ts 2 src/renderer/src/store/github/project-row-actions.ts 1 src/renderer/src/store/github/pull-request-execution.ts +2 src/renderer/src/store/github/refresh-routing-actions.ts 3 src/renderer/src/store/github/repository-routing.ts -1 src/renderer/src/store/github/visible-hosted-review-refresh-targets.ts +2 src/renderer/src/store/github/visible-hosted-review-refresh-targets.ts +2 src/renderer/src/store/github/work-item-aggregate-actions.ts +3 src/renderer/src/store/github/work-item-fetch-actions.ts 1 src/renderer/src/store/github/work-item-mutation-actions.ts -1 src/renderer/src/store/github/work-item-routing.ts -4 src/renderer/src/store/index.ts +3 src/renderer/src/store/github/work-item-routing.ts +9 src/renderer/src/store/index.ts 6 src/renderer/src/store/project-groups/nested-repository-operations.ts 2 src/renderer/src/store/project-groups/project-group-catalog-actions.ts 7 src/renderer/src/store/project-groups/project-group-mutations.ts @@ -481,10 +557,17 @@ 2 src/renderer/src/store/repos/repo-update.ts 2 src/renderer/src/store/repos/runtime-repo-catalog-actions.ts 2 src/renderer/src/store/repos/safe-auto-fork-sync.ts -1 src/renderer/src/store/slices/browser/browser-host-state.ts -2 src/renderer/src/store/slices/browser/browser-tab-actions.ts -2 src/renderer/src/store/slices/browser/paired-browser-tab-creator.ts -1 src/renderer/src/store/slices/diff-comment-persistence.ts +1 src/renderer/src/store/slices/browser-slice-test-harness.ts +14 src/renderer/src/store/slices/browser/browser-cookie-import-actions.ts +1 src/renderer/src/store/slices/browser/browser-host-actions.ts +7 src/renderer/src/store/slices/browser/browser-host-state.ts +1 src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts +12 src/renderer/src/store/slices/browser/browser-profile-import-actions.ts +11 src/renderer/src/store/slices/browser/browser-profile-list-actions.ts +4 src/renderer/src/store/slices/browser/browser-tab-actions.ts +1 src/renderer/src/store/slices/browser/create-browser-slice.ts +3 src/renderer/src/store/slices/diff-comment-persistence.ts +2 src/renderer/src/store/slices/editor-slice-test-harness.ts 4 src/renderer/src/store/slices/editor/actions/git-remote-push-pull.ts 1 src/renderer/src/store/slices/editor/actions/git-remote-status.ts 8 src/renderer/src/store/slices/editor/actions/git-remote-sync.ts @@ -495,27 +578,42 @@ 2 src/renderer/src/store/slices/editor/create-editor-slice.ts 1 src/renderer/src/store/slices/editor/tabs/untitled-file-cleanup.ts 1 src/renderer/src/store/slices/github-cache-key.ts -1 src/renderer/src/store/slices/github-slice-test-harness.ts -3 src/renderer/src/store/slices/github.ts +2 src/renderer/src/store/slices/github-slice-test-harness.ts +1 src/renderer/src/store/slices/github.ts 1 src/renderer/src/store/slices/hosted-review-cache-identity.ts 4 src/renderer/src/store/slices/hosted-review.ts -8 src/renderer/src/store/slices/jira-connection-actions.ts -4 src/renderer/src/store/slices/jira-read-coordination.ts -5 src/renderer/src/store/slices/linear/linear-slice-connection-actions.ts -3 src/renderer/src/store/slices/linear/linear-slice-scope.ts -1 src/renderer/src/store/slices/linear/linear-slice-status-actions.ts +1 src/renderer/src/store/slices/jira-collection-read-actions.ts +13 src/renderer/src/store/slices/jira-connection-actions.ts +4 src/renderer/src/store/slices/jira-issue-read-actions.ts +5 src/renderer/src/store/slices/jira-read-coordination.ts +3 src/renderer/src/store/slices/linear/linear-custom-view-detail-actions.ts +3 src/renderer/src/store/slices/linear/linear-custom-view-issue-actions.ts +3 src/renderer/src/store/slices/linear/linear-custom-view-list-actions.ts +3 src/renderer/src/store/slices/linear/linear-custom-view-project-actions.ts +3 src/renderer/src/store/slices/linear/linear-issue-detail-actions.ts +6 src/renderer/src/store/slices/linear/linear-issue-list-actions.ts +3 src/renderer/src/store/slices/linear/linear-project-detail-actions.ts +3 src/renderer/src/store/slices/linear/linear-project-issue-actions.ts +3 src/renderer/src/store/slices/linear/linear-project-list-actions.ts +13 src/renderer/src/store/slices/linear/linear-slice-connection-actions.ts +4 src/renderer/src/store/slices/linear/linear-slice-scope.ts +3 src/renderer/src/store/slices/linear/linear-slice-status-actions.ts +3 src/renderer/src/store/slices/linear/linear-team-actions.ts 2 src/renderer/src/store/slices/preflight.ts -2 src/renderer/src/store/slices/project-group-owner-routing.ts +1 src/renderer/src/store/slices/project-group-owner-routing.ts 1 src/renderer/src/store/slices/repo-host-identity.ts 1 src/renderer/src/store/slices/repo-reorder-host-split.ts -12 src/renderer/src/store/slices/repos.ts +10 src/renderer/src/store/slices/repos.ts 1 src/renderer/src/store/slices/runtime-status.ts 1 src/renderer/src/store/slices/settings-owner-hydration-publication.ts 5 src/renderer/src/store/slices/settings.ts -4 src/renderer/src/store/slices/store-test-helpers.ts +8 src/renderer/src/store/slices/store-test-helpers.ts +2 src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts +2 src/renderer/src/store/slices/terminal-tab-retirement.ts 1 src/renderer/src/store/slices/worktree-visibility-owner-settings.ts 3 src/renderer/src/store/slices/worktree-visibility-settings-write.ts -7 src/renderer/src/store/slices/worktrees.ts +3 src/renderer/src/store/slices/worktrees-slice-test-harness.ts +13 src/renderer/src/store/slices/worktrees.ts 2 src/renderer/src/store/slices/worktrees/create/create-worktree.ts 2 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts 2 src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts @@ -523,21 +621,24 @@ 4 src/renderer/src/store/slices/worktrees/listing/fetch-all-worktrees.ts 2 src/renderer/src/store/slices/worktrees/listing/fetch-detected-worktrees.ts 5 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts -2 src/renderer/src/store/slices/worktrees/listing/known-ssh-worktree-fetch.ts +1 src/renderer/src/store/slices/worktrees/listing/known-ssh-worktree-fetch.ts 1 src/renderer/src/store/slices/worktrees/listing/worktree-host-ownership.ts 2 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts +2 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target-ensure.ts 4 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts -2 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts +4 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts 3 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts 7 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts 1 src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.ts 2 src/renderer/src/store/slices/worktrees/session/worktree-terminal-reference.ts 1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts 1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts -1 src/renderer/src/store/slices/worktrees/teardown/remove-worktree.ts -6 src/renderer/src/web/preload-api/web-github-api.ts -8 src/renderer/src/web/preload-api/web-github-routes.ts -2 src/renderer/src/web/preload-api/web-preference-normalization.ts -15 src/renderer/src/web/preload-api/web-preferences-store.ts +2 src/renderer/src/store/slices/worktrees/teardown/remove-worktree.ts +1 src/renderer/src/store/terminals/terminal-active-workspace-creation.ts +1 src/renderer/src/store/terminals/terminal-pane-hibernation.ts +1 src/renderer/src/store/terminals/terminal-shutdown.ts +1 src/renderer/src/store/terminals/terminal-workspace-routing.ts +3 src/renderer/src/web/preload-api/web-preference-normalization.ts +16 src/renderer/src/web/preload-api/web-preferences-store.ts 6 src/renderer/src/web/preload-api/web-settings-api.ts 1 src/renderer/src/web/web-preload-api.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index 7803f2d90d76..8b1251291f83 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -37,8 +37,8 @@ const SETTING_DESTRUCTURE_READ = // A top-level declaration: `[export] [async] function name` or `[export] const|let name =`. const TOP_LEVEL_DECLARATION = /^(export\s+)?(?:default\s+)?(?:async\s+)?(?:function\s*\*?\s*([\w$]+)|(?:const|let)\s+([\w$]+)\b)/gm -const TOP_LEVEL_BOUNDARY = - /^(?:export|import|function|async|const|let|type|interface|class|enum)\b/gm +// Any new statement at column 0 ends a declaration; closing brackets and continuations do not. +const TOP_LEVEL_BOUNDARY = /^[^\s)}\]]/gm function readerUsePattern(names) { return names.size === 0 @@ -69,41 +69,207 @@ export function topLevelDeclarations(sourceText) { }) } +// Statements that hand a value on: `return …` or an arrow's expression body, each running to the +// first newline outside brackets. Object-literal results (`=> ({ … })`, `return { … }`) are +// skipped: slice creators and hooks return bags of actions, which pulls in the whole store. +const VALUE_STATEMENT_START = /\breturn\s+(?!\(?\s*\{)|=>\s*(?!\(?\s*\{)/g + +function valueStatements(body) { + const statements = [] + for (const match of body.matchAll(VALUE_STATEMENT_START)) { + let depth = 0 + let end = match.index + match[0].length + for (; end < body.length; end += 1) { + const char = body[end] + if ('([{'.includes(char)) { + depth += 1 + } else if (')]}'.includes(char)) { + if (depth === 0) { + break + } + depth -= 1 + } else if (char === '\n' && depth === 0) { + break + } + } + statements.push(body.slice(match.index, end)) + } + return statements.join('\n') +} + +const IMPORT_STATEMENT = + /\bimport\s+(?:type\s+)?(?:[\w$]+\s*,\s*)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g +const REEXPORT_STATEMENT = /\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g +const STAR_REEXPORT_STATEMENT = /\bexport\s+\*\s+from\s*['"]([^'"]+)['"]/g +const LOCAL_EXPORT_LIST = /\bexport\s+\{([^}]*)\}(?!\s*from)/g + +function specifiers(list) { + return list + .split(',') + .map((part) => part.trim().replace(/^type\s+/, '')) + .filter(Boolean) + .map((part) => { + const [imported, local = imported] = part.split(/\s+as\s+/) + return { imported: imported.trim(), local: local.trim() } + }) +} + +function resolveModule(spec, fromRel, files) { + let base + if (spec.startsWith('@/')) { + base = `${SCAN_ROOT}/${spec.slice(2)}` + } else if (spec.startsWith('.')) { + base = path.posix.join(path.posix.dirname(fromRel), spec) + } else { + return null + } + base = base.replace(/\.(?:m?js|tsx?)$/, '') + return ( + ['.ts', '.tsx', '/index.ts', '/index.tsx'].map((ext) => base + ext).find((c) => files.has(c)) ?? + null + ) +} + +function parseModule(rel, text, files) { + const imports = new Map() + for (const [, list, spec] of text.matchAll(IMPORT_STATEMENT)) { + const from = resolveModule(spec, rel, files) + for (const { imported, local } of specifiers(list)) { + imports.set(local, { from, name: imported }) + } + } + const reexports = new Map() + for (const [, list, spec] of text.matchAll(REEXPORT_STATEMENT)) { + const from = resolveModule(spec, rel, files) + for (const { imported, local } of specifiers(list)) { + reexports.set(local, { from, name: imported }) + } + } + const starFrom = [...text.matchAll(STAR_REEXPORT_STATEMENT)].map(([, spec]) => + resolveModule(spec, rel, files) + ) + const listed = new Map() + for (const [, list] of text.matchAll(LOCAL_EXPORT_LIST)) { + for (const { imported, local } of specifiers(list)) { + listed.set(imported, local) + } + } + const declarations = topLevelDeclarations(text.replace(IMPORT_EXPORT_LIST, '')).map((decl) => ({ + ...decl, + exportedAs: decl.exported ? decl.name : (listed.get(decl.name) ?? null) + })) + return { imports, reexports, starFrom, declarations } +} + /** - * Functions that read the setting for their caller: the seeds plus every exported top-level - * function whose own body reads the setting or calls a seed. Hand lists miss look-alikes such as - * a copy of `getActiveRuntimeTarget` under another name; discovery does not. One level only: - * following calls transitively pulls in most of the renderer. + * Functions that read the setting for their caller, keyed by defining file so a same-named + * function elsewhere (a test harness's `useAppStore`) is not mistaken for one. Seeds, then every + * exported top-level function whose body reads the setting or calls a seed, then, to a fixpoint, + * every one that returns a reader's result. Hand lists miss look-alikes such as a copy of + * `getActiveRuntimeTarget` under another name; discovery does not. Following every call instead + * of returned values pulls in most of the renderer through the store. */ export function discoverFocusReaders(sources) { - const readers = new Set(SEED_FOCUS_READERS) - const seedUse = readerUsePattern(new Set(SEED_FOCUS_READERS)) - for (const text of sources.values()) { - for (const { name, exported, text: body } of topLevelDeclarations( - text.replace(IMPORT_EXPORT_LIST, '') - )) { + const files = new Set(sources.keys()) + const modules = new Map([...sources].map(([rel, text]) => [rel, parseModule(rel, text, files)])) + const seeds = new Set(SEED_FOCUS_READERS) + const seedUse = readerUsePattern(seeds) + const readerKeys = new Set() + + const resolveExport = (rel, name, depth = 0) => { + const mod = rel ? modules.get(rel) : null + if (!mod || depth > 8) { + return null + } + if (mod.declarations.some((decl) => decl.exportedAs === name)) { + return `${rel}#${name}` + } + const re = mod.reexports.get(name) + if (re) { + return resolveExport(re.from, re.name, depth + 1) + } + for (const from of mod.starFrom) { + const key = resolveExport(from, name, depth + 1) + if (key) { + return key + } + } + return null + } + const resolveLocal = (rel, name) => { + const mod = modules.get(rel) + if (mod.declarations.some((decl) => decl.exportedAs && decl.name === name)) { + return `${rel}#${mod.declarations.find((decl) => decl.name === name).exportedAs}` + } + const imported = mod.imports.get(name) + return imported ? resolveExport(imported.from, imported.name) : null + } + const isReader = (rel, name) => seeds.has(name) || readerKeys.has(resolveLocal(rel, name)) + + const pending = [] + for (const [rel, mod] of modules) { + for (const { name, exportedAs, text } of mod.declarations) { + // Components render; a `` that reads focus inside is not a read by its parent. + if (!exportedAs || /^[A-Z]/.test(exportedAs)) { + continue + } seedUse.lastIndex = 0 - if (exported && (countSettingReads(body) > 0 || seedUse.test(body))) { - readers.add(name) + if (countSettingReads(text) > 0 || seedUse.test(text)) { + readerKeys.add(`${rel}#${exportedAs}`) + continue } + const passed = new Set(valueStatements(text).match(/[\w$]+/g)) + passed.delete(name) + pending.push({ rel, key: `${rel}#${exportedAs}`, passed: [...passed] }) + } + } + let changed = true + while (changed) { + changed = false + for (let i = pending.length - 1; i >= 0; i -= 1) { + const { rel, key, passed } = pending[i] + if (passed.some((id) => isReader(rel, id))) { + readerKeys.add(key) + pending.splice(i, 1) + changed = true + } + } + } + + const namesByFile = new Map() + return { + has: (name) => seeds.has(name) || [...readerKeys].some((key) => key.endsWith(`#${name}`)), + /** Names that refer to a reader inside `rel`: seeds, its own readers and imported ones. */ + namesFor(rel) { + if (!namesByFile.has(rel)) { + const mod = modules.get(rel) + const local = [ + ...(mod?.declarations.map((decl) => decl.name) ?? []), + ...(mod?.imports.keys() ?? []) + ] + namesByFile.set( + rel, + new Set([...seeds, ...local.filter((name) => mod && isReader(rel, name))]) + ) + } + return namesByFile.get(rel) } } - return readers } /** - * Reads of the setting (member, element and destructuring reads) plus every call of a function - * that reads it for the caller. Object-literal keys are writes and are not counted. + * Reads of the setting (member, element and destructuring reads) plus every use of a name that + * refers to a reader. Object-literal keys are writes and are not counted. */ -export function countFocusSettingReads(sourceText, readers = new Set(SEED_FOCUS_READERS)) { +export function countFocusSettingReads(sourceText, readerNames = new Set(SEED_FOCUS_READERS)) { const body = sourceText.replace(IMPORT_EXPORT_LIST, '') - const pattern = readerUsePattern(readers) + const pattern = readerUsePattern(readerNames) return countSettingReads(body) + (pattern ? (body.match(pattern)?.length ?? 0) : 0) } -/** An alias hides later calls from the count, so it is refused outright. */ -export function hasFocusRoutingAlias(sourceText, readers = new Set(SEED_FOCUS_READERS)) { - const alias = new RegExp(`\\b(?:${[...readers].join('|')})\\s+as\\b`) +/** An alias hides later calls from the name-based helper count, so it is refused outright. */ +export function hasFocusRoutingAlias(sourceText) { + const alias = new RegExp(`\\b(?:${SEED_FOCUS_READERS.join('|')})\\s+as\\b`) return (sourceText.match(IMPORT_EXPORT_LIST) ?? []).some((list) => alias.test(list)) } @@ -124,7 +290,7 @@ export const RATCHETS = [ { name: 'focus-setting-read', baselinePath: 'config/focus-setting-read-baseline.txt', - count: (text, _rel, readers) => countFocusSettingReads(text, readers), + count: (text, rel, readers) => countFocusSettingReads(text, readers.namesFor(rel)), header: [ '# Renderer reads of the Active Server setting (member, element and destructuring reads of', '# activeRuntimeEnvironmentId) plus calls of exported functions that read it, per file.', @@ -213,11 +379,21 @@ function readTrackedSources(root, scanRoot) { return sources } +// One read and one discovery per root per run; `--prune` then `main` reuse them. +const scanCache = new Map() + +function scanSources(root) { + if (!scanCache.has(root)) { + const sources = new Map( + FOCUS_SCAN_ROOTS.flatMap((scanRoot) => [...readTrackedSources(root, scanRoot)]) + ) + scanCache.set(root, { sources, readers: discoverFocusReaders(sources) }) + } + return scanCache.get(root) +} + export function collectCurrentCounts(root = process.cwd(), count = RATCHETS[0].count) { - const sources = new Map( - FOCUS_SCAN_ROOTS.flatMap((scanRoot) => [...readTrackedSources(root, scanRoot)]) - ) - const readers = discoverFocusReaders(sources) + const { sources, readers } = scanSources(root) const counts = new Map() const aliased = [] for (const [rel, source] of sources) { @@ -228,7 +404,7 @@ export function collectCurrentCounts(root = process.cwd(), count = RATCHETS[0].c if (found > 0) { counts.set(rel, found) } - if (hasFocusRoutingAlias(source, readers)) { + if (hasFocusRoutingAlias(source)) { aliased.push(rel) } } diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index 930517d2b080..baceb2027bfe 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -56,7 +56,7 @@ describe('countFocusSettingReads', () => { expect(countFocusSettingReads(src)).toBe(5) }) - it('discovers look-alike readers, so swapping a helper for one lowers nothing', () => { + it('discovers readers through imports, so a look-alike or a wrapper lowers nothing', () => { const readers = discoverFocusReaders( new Map([ [ @@ -66,34 +66,65 @@ describe('countFocusSettingReads', () => { [ 'src/renderer/src/a.ts', [ + "import { getSettingsFocusedExecutionHostId } from '../../shared/execution-host'", 'export function getAutomationListTarget(settings) {', ' const id = settings?.activeRuntimeEnvironmentId?.trim()', " return id ? { kind: 'environment', environmentId: id } : { kind: 'local' }", '}', 'export const cacheKey = (s) => `k:${getActiveRuntimeTarget(s).kind}`', - 'function localOnly(s) {', - ' return s.activeRuntimeEnvironmentId', + 'export function browserHostId(state, override) {', + ' return override ?? getSettingsFocusedExecutionHostId(state.settings)', + '}', + 'export function wrappedTwice(state) {', + ' return browserHostId(state, null)', '}', 'export function ownerTarget(row) {', ' return row.owner', + '}', + 'export function SettingsPane() {', + ' return getAutomationListTarget(null)', '}' ].join('\n') + ], + [ + 'src/renderer/src/harness.ts', + 'export const ownerTarget = (s) => s.activeRuntimeEnvironmentId' + ], + [ + 'src/renderer/src/b.ts', + [ + "import { browserHostId, wrappedTwice, ownerTarget, SettingsPane } from './a'", + 'const a = wrappedTwice(state)', + 'const b = ownerTarget(row)', + 'const c = ' + ].join('\n') ] ]) ) - expect(readers.has('getAutomationListTarget')).toBe(true) - expect(readers.has('getSettingsFocusedExecutionHostId')).toBe(true) - expect(readers.has('cacheKey')).toBe(true) - expect(readers.has('localOnly')).toBe(false) - expect(readers.has('ownerTarget')).toBe(false) - expect(countFocusSettingReads('const t = getAutomationListTarget(settings)', readers)).toBe(1) + const inA = readers.namesFor('src/renderer/src/a.ts') + for (const name of ['getAutomationListTarget', 'cacheKey', 'browserHostId', 'wrappedTwice']) { + expect(inA.has(name)).toBe(true) + } + expect(inA.has('ownerTarget')).toBe(false) + expect(inA.has('SettingsPane')).toBe(false) + // `ownerTarget` from `./a` is not the harness's same-named reader. + expect( + [...readers.namesFor('src/renderer/src/b.ts')].filter((n) => !n.startsWith('get')) + ).toEqual(expect.arrayContaining(['browserHostId', 'wrappedTwice'])) + expect(readers.namesFor('src/renderer/src/b.ts').has('ownerTarget')).toBe(false) expect( countFocusSettingReads( 'const t = runtimeTargetForOwnerHostId(getSettingsFocusedExecutionHostId(s))', - readers + readers.namesFor('src/renderer/src/a.ts') + ) + ).toBe(1) + expect( + countFocusSettingReads( + "import { wrappedTwice } from './a'\nconst t = wrappedTwice(state)", + readers.namesFor('src/renderer/src/b.ts') ) ).toBe(1) - expect(countFocusSettingReads('export const cacheKey = (s) => s', readers)).toBe(0) + expect(countFocusSettingReads('export const cacheKey = (s) => s', inA)).toBe(0) }) it('counts destructuring reads', () => { @@ -130,12 +161,6 @@ describe('hasFocusRoutingAlias', () => { hasFocusRoutingAlias("export {\n settingsForRuntimeOwner as owner\n} from './target'") ).toBe(true) expect(hasFocusRoutingAlias("import { defaultCreationHost as host } from './d'")).toBe(true) - expect( - hasFocusRoutingAlias( - "import { getAutomationListTarget as h } from './e'", - new Set(['getAutomationListTarget']) - ) - ).toBe(true) expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget } from './rpc'")).toBe(false) }) }) From 3a860d8e1139cbcf6fa2b306d8bed6db85d16898 Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 10:09:17 -0700 Subject: [PATCH 6/7] fix(lint): a reader called through a namespace or dynamic import still counts --- config/focus-setting-read-baseline.txt | 10 ++-- .../scripts/check-owner-routing-ratchet.mjs | 54 +++++++++++++++---- .../check-owner-routing-ratchet.test.mjs | 34 ++++++++++++ 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index f9c5f81a8219..ff07bbf5d1c4 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -146,7 +146,7 @@ 3 src/renderer/src/components/pull-request-page/edit/issue-updates.ts 8 src/renderer/src/components/pull-request-page/edit/section.tsx 4 src/renderer/src/components/pull-request-page/reviewers/panel.tsx -2 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts +4 src/renderer/src/components/pull-request-page/reviewers/request-actions.ts 2 src/renderer/src/components/quick-open-file-list-test-harness.ts 6 src/renderer/src/components/quick-open-file-list.ts 4 src/renderer/src/components/quick-open-file-navigation.ts @@ -321,7 +321,7 @@ 1 src/renderer/src/components/tab-group/AiVaultSessionDropLayer.tsx 3 src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts 5 src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts -2 src/renderer/src/components/task-page-github-reviewer-actions.ts +4 src/renderer/src/components/task-page-github-reviewer-actions.ts 2 src/renderer/src/components/task-page/TaskPage.tsx 2 src/renderer/src/components/task-page/github/AssigneesCell.tsx 2 src/renderer/src/components/task-page/github/MergeCell.tsx @@ -389,6 +389,7 @@ 1 src/renderer/src/hooks/editor-external-watch-disk-verification.ts 2 src/renderer/src/hooks/editor-external-watch-targets.ts 1 src/renderer/src/hooks/editor-runtime-file-watch.ts +6 src/renderer/src/hooks/ipc-events-test-harness.ts 2 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts @@ -564,8 +565,9 @@ 1 src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts 12 src/renderer/src/store/slices/browser/browser-profile-import-actions.ts 11 src/renderer/src/store/slices/browser/browser-profile-list-actions.ts -4 src/renderer/src/store/slices/browser/browser-tab-actions.ts +6 src/renderer/src/store/slices/browser/browser-tab-actions.ts 1 src/renderer/src/store/slices/browser/create-browser-slice.ts +1 src/renderer/src/store/slices/browser/paired-browser-tab-creator.ts 3 src/renderer/src/store/slices/diff-comment-persistence.ts 2 src/renderer/src/store/slices/editor-slice-test-harness.ts 4 src/renderer/src/store/slices/editor/actions/git-remote-push-pull.ts @@ -634,7 +636,7 @@ 1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts 1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts 2 src/renderer/src/store/slices/worktrees/teardown/remove-worktree.ts -1 src/renderer/src/store/terminals/terminal-active-workspace-creation.ts +2 src/renderer/src/store/terminals/terminal-active-workspace-creation.ts 1 src/renderer/src/store/terminals/terminal-pane-hibernation.ts 1 src/renderer/src/store/terminals/terminal-shutdown.ts 1 src/renderer/src/store/terminals/terminal-workspace-routing.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index 8b1251291f83..dd901e773b81 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -40,6 +40,9 @@ const TOP_LEVEL_DECLARATION = // Any new statement at column 0 ends a declaration; closing brackets and continuations do not. const TOP_LEVEL_BOUNDARY = /^[^\s)}\]]/gm +// Member calls only, so dotted string keys (`'auto.hooks.useX.abc'`) are not uses. +const MEMBER_NAME = /\.\s*([\w$]+)\s*(?:\?\.\s*)?\(/g + function readerUsePattern(names) { return names.size === 0 ? null @@ -100,6 +103,8 @@ function valueStatements(body) { const IMPORT_STATEMENT = /\bimport\s+(?:type\s+)?(?:[\w$]+\s*,\s*)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g const REEXPORT_STATEMENT = /\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g +// `const { reader } = await import('…')` binds like a named import. +const DYNAMIC_IMPORT_DESTRUCTURE = /\{([^{}]*)\}\s*=\s*await\s+import\(\s*['"]([^'"]+)['"]\s*\)/g const STAR_REEXPORT_STATEMENT = /\bexport\s+\*\s+from\s*['"]([^'"]+)['"]/g const LOCAL_EXPORT_LIST = /\bexport\s+\{([^}]*)\}(?!\s*from)/g @@ -132,7 +137,10 @@ function resolveModule(spec, fromRel, files) { function parseModule(rel, text, files) { const imports = new Map() - for (const [, list, spec] of text.matchAll(IMPORT_STATEMENT)) { + for (const [, list, spec] of [ + ...text.matchAll(IMPORT_STATEMENT), + ...text.matchAll(DYNAMIC_IMPORT_DESTRUCTURE) + ]) { const from = resolveModule(spec, rel, files) for (const { imported, local } of specifiers(list)) { imports.set(local, { from, name: imported }) @@ -218,18 +226,32 @@ export function discoverFocusReaders(sources) { readerKeys.add(`${rel}#${exportedAs}`) continue } - const passed = new Set(valueStatements(text).match(/[\w$]+/g)) + const statements = valueStatements(text) + const passed = new Set(statements.match(/[\w$]+/g)) passed.delete(name) - pending.push({ rel, key: `${rel}#${exportedAs}`, passed: [...passed] }) + const members = [...statements.matchAll(MEMBER_NAME)].map(([, member]) => member) + pending.push({ rel, key: `${rel}#${exportedAs}`, passed: [...passed], members }) } } + // Exported reader names, for `ns.reader(…)` through a namespace or dynamic import. + const memberNames = new Set(seeds) + const addReader = (key) => { + readerKeys.add(key) + memberNames.add(key.slice(key.indexOf('#') + 1)) + } + for (const key of readerKeys) { + addReader(key) + } let changed = true while (changed) { changed = false for (let i = pending.length - 1; i >= 0; i -= 1) { - const { rel, key, passed } = pending[i] - if (passed.some((id) => isReader(rel, id))) { - readerKeys.add(key) + const { rel, key, passed, members } = pending[i] + if ( + passed.some((id) => isReader(rel, id)) || + members.some((member) => memberNames.has(member)) + ) { + addReader(key) pending.splice(i, 1) changed = true } @@ -238,7 +260,8 @@ export function discoverFocusReaders(sources) { const namesByFile = new Map() return { - has: (name) => seeds.has(name) || [...readerKeys].some((key) => key.endsWith(`#${name}`)), + has: (name) => memberNames.has(name), + memberNames, /** Names that refer to a reader inside `rel`: seeds, its own readers and imported ones. */ namesFor(rel) { if (!namesByFile.has(rel)) { @@ -261,10 +284,18 @@ export function discoverFocusReaders(sources) { * Reads of the setting (member, element and destructuring reads) plus every use of a name that * refers to a reader. Object-literal keys are writes and are not counted. */ -export function countFocusSettingReads(sourceText, readerNames = new Set(SEED_FOCUS_READERS)) { - const body = sourceText.replace(IMPORT_EXPORT_LIST, '') +export function countFocusSettingReads( + sourceText, + readerNames = new Set(SEED_FOCUS_READERS), + memberNames = new Set() +) { + const body = sourceText.replace(IMPORT_EXPORT_LIST, '').replace(DYNAMIC_IMPORT_DESTRUCTURE, '') const pattern = readerUsePattern(readerNames) - return countSettingReads(body) + (pattern ? (body.match(pattern)?.length ?? 0) : 0) + // `ns.reader(…)` through a namespace or dynamic import; names already counted bare are skipped. + const members = [...body.matchAll(MEMBER_NAME)].filter( + ([, member]) => memberNames.has(member) && !readerNames.has(member) + ).length + return countSettingReads(body) + (pattern ? (body.match(pattern)?.length ?? 0) : 0) + members } /** An alias hides later calls from the name-based helper count, so it is refused outright. */ @@ -290,7 +321,8 @@ export const RATCHETS = [ { name: 'focus-setting-read', baselinePath: 'config/focus-setting-read-baseline.txt', - count: (text, rel, readers) => countFocusSettingReads(text, readers.namesFor(rel)), + count: (text, rel, readers) => + countFocusSettingReads(text, readers.namesFor(rel), readers.memberNames), header: [ '# Renderer reads of the Active Server setting (member, element and destructuring reads of', '# activeRuntimeEnvironmentId) plus calls of exported functions that read it, per file.', diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index baceb2027bfe..ccb58a81e47e 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -127,6 +127,40 @@ describe('countFocusSettingReads', () => { expect(countFocusSettingReads('export const cacheKey = (s) => s', inA)).toBe(0) }) + it('counts a reader called through a namespace or dynamic import', () => { + const readers = discoverFocusReaders( + new Map([ + [ + 'src/renderer/src/routing.ts', + 'export function focusedHostId(s) {\n return s.activeRuntimeEnvironmentId\n}\n' + ], + [ + 'src/renderer/src/wrap.ts', + "import * as rr from './routing'\nexport function hostOf(s) {\n return rr.focusedHostId(s)\n}\n" + ], + [ + 'src/renderer/src/use.ts', + "const { focusedHostId } = await import('./routing')\nconst a = focusedHostId(s)\n" + ] + ]) + ) + expect(readers.has('hostOf')).toBe(true) + expect( + countFocusSettingReads( + "import * as rr from './routing'\nconst a = rr.focusedHostId(s)\nt('auto.rr.focusedHostId.x')", + readers.namesFor('src/renderer/src/other.ts'), + readers.memberNames + ) + ).toBe(1) + expect( + countFocusSettingReads( + "const { focusedHostId } = await import('./routing')\nconst a = focusedHostId(s)\n", + readers.namesFor('src/renderer/src/use.ts'), + readers.memberNames + ) + ).toBe(1) + }) + it('counts destructuring reads', () => { expect(countFocusSettingReads('const { activeRuntimeEnvironmentId } = s')).toBe(1) expect( From 14dffd6229f0ed85003f93919dbe1616477cda7b Mon Sep 17 00:00:00 2001 From: m4air Date: Sat, 10 Oct 2026 10:13:08 -0700 Subject: [PATCH 7/7] fix(lint): renamed, parenthesized and .then() dynamic imports bind readers too --- config/focus-setting-read-baseline.txt | 2 ++ .../scripts/check-owner-routing-ratchet.mjs | 19 +++++++++++----- .../check-owner-routing-ratchet.test.mjs | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index ff07bbf5d1c4..75fe522ca4f7 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -389,6 +389,8 @@ 1 src/renderer/src/hooks/editor-external-watch-disk-verification.ts 2 src/renderer/src/hooks/editor-external-watch-targets.ts 1 src/renderer/src/hooks/editor-runtime-file-watch.ts +1 src/renderer/src/hooks/ipc-events-close-routing-test-harness.ts +1 src/renderer/src/hooks/ipc-events-terminal-create-test-harness.ts 6 src/renderer/src/hooks/ipc-events-test-harness.ts 2 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts 1 src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts diff --git a/config/scripts/check-owner-routing-ratchet.mjs b/config/scripts/check-owner-routing-ratchet.mjs index dd901e773b81..0c78a91b6c5a 100644 --- a/config/scripts/check-owner-routing-ratchet.mjs +++ b/config/scripts/check-owner-routing-ratchet.mjs @@ -103,8 +103,12 @@ function valueStatements(body) { const IMPORT_STATEMENT = /\bimport\s+(?:type\s+)?(?:[\w$]+\s*,\s*)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g const REEXPORT_STATEMENT = /\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g -// `const { reader } = await import('…')` binds like a named import. -const DYNAMIC_IMPORT_DESTRUCTURE = /\{([^{}]*)\}\s*=\s*await\s+import\(\s*['"]([^'"]+)['"]\s*\)/g +// `const { reader } = (await import('…'))` and `import('…').then(({ reader }) =>` bind like a +// named import; `{ reader: local }` renames like `as`. +const DYNAMIC_IMPORT_DESTRUCTURE = + /\{([^{}]*)\}\s*=\s*\(?\s*await\s+import\(\s*['"]([^'"]+)['"]\s*\)(?:\s*\))?/g +const DYNAMIC_IMPORT_THEN = + /\bimport\(\s*['"]([^'"]+)['"]\s*\)\s*\.then\(\s*(?:async\s*)?\(\s*\{([^{}]*)\}/g const STAR_REEXPORT_STATEMENT = /\bexport\s+\*\s+from\s*['"]([^'"]+)['"]/g const LOCAL_EXPORT_LIST = /\bexport\s+\{([^}]*)\}(?!\s*from)/g @@ -114,7 +118,7 @@ function specifiers(list) { .map((part) => part.trim().replace(/^type\s+/, '')) .filter(Boolean) .map((part) => { - const [imported, local = imported] = part.split(/\s+as\s+/) + const [imported, local = imported] = part.split(/\s+as\s+|\s*:\s*/) return { imported: imported.trim(), local: local.trim() } }) } @@ -139,7 +143,8 @@ function parseModule(rel, text, files) { const imports = new Map() for (const [, list, spec] of [ ...text.matchAll(IMPORT_STATEMENT), - ...text.matchAll(DYNAMIC_IMPORT_DESTRUCTURE) + ...text.matchAll(DYNAMIC_IMPORT_DESTRUCTURE), + ...[...text.matchAll(DYNAMIC_IMPORT_THEN)].map(([all, from, names]) => [all, names, from]) ]) { const from = resolveModule(spec, rel, files) for (const { imported, local } of specifiers(list)) { @@ -289,7 +294,11 @@ export function countFocusSettingReads( readerNames = new Set(SEED_FOCUS_READERS), memberNames = new Set() ) { - const body = sourceText.replace(IMPORT_EXPORT_LIST, '').replace(DYNAMIC_IMPORT_DESTRUCTURE, '') + const body = sourceText + .replace(IMPORT_EXPORT_LIST, '') + // A placeholder, so a dangling `const` does not read as a declaration of the next call. + .replace(DYNAMIC_IMPORT_DESTRUCTURE, 'dynamicImport') + .replace(DYNAMIC_IMPORT_THEN, 'dynamicImport') const pattern = readerUsePattern(readerNames) // `ns.reader(…)` through a namespace or dynamic import; names already counted bare are skipped. const members = [...body.matchAll(MEMBER_NAME)].filter( diff --git a/config/scripts/check-owner-routing-ratchet.test.mjs b/config/scripts/check-owner-routing-ratchet.test.mjs index ccb58a81e47e..89dc6e2b5c24 100644 --- a/config/scripts/check-owner-routing-ratchet.test.mjs +++ b/config/scripts/check-owner-routing-ratchet.test.mjs @@ -159,6 +159,28 @@ describe('countFocusSettingReads', () => { readers.memberNames ) ).toBe(1) + for (const [rel, text] of [ + ['src/renderer/src/r1.ts', "const { focusedHostId: g } = await import('./routing')\ng(s)"], + [ + 'src/renderer/src/r2.ts', + "const { focusedHostId } = (await import('./routing'))\nfocusedHostId(s)" + ], + [ + 'src/renderer/src/r3.ts', + "void import('./routing').then(({ focusedHostId }) => focusedHostId(s))" + ] + ]) { + const withFile = discoverFocusReaders( + new Map([ + [ + 'src/renderer/src/routing.ts', + 'export function focusedHostId(s) {\n return s.activeRuntimeEnvironmentId\n}\n' + ], + [rel, text] + ]) + ) + expect(countFocusSettingReads(text, withFile.namesFor(rel), withFile.memberNames)).toBe(1) + } }) it('counts destructuring reads', () => {