Skip to content

Commit f0a6eb1

Browse files
m4airm4air
authored andcommitted
fix(lint): the focus-read ratchet counts shared focus helpers and destructuring reads
1 parent f643022 commit f0a6eb1

3 files changed

Lines changed: 96 additions & 27 deletions

File tree

‎config/focus-setting-read-baseline.txt‎

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the
2-
# focus-routing helpers and defaultCreationHost(, per file.
1+
# Renderer reads of the Active Server setting (member, element and destructuring reads of
2+
# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file.
33
# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read
44
# the default host, through defaultCreationHost. Everything else routes by the owner.
55
# Prune after removing reads: pnpm check:owner-routing-ratchet --prune
66
1 src/renderer/src/components/LinearIssueWorkspace.tsx
7+
2 src/renderer/src/components/activity/activity-thread-actions.ts
8+
1 src/renderer/src/components/activity/use-agent-pane-threads.ts
79
2 src/renderer/src/components/agent/AgentSettingsDialog.tsx
810
1 src/renderer/src/components/automations/automation-host-client.ts
911
2 src/renderer/src/components/automations/use-automations-page-setup-state.ts
12+
1 src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-stream-activation.ts
1013
1 src/renderer/src/components/cmd-j/quick-action-context.ts
1114
2 src/renderer/src/components/editor/EditorPanel.tsx
1215
1 src/renderer/src/components/editor/ExternalFileChangeBanner.tsx
@@ -57,6 +60,7 @@
5760
1 src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.ts
5861
1 src/renderer/src/components/right-sidebar/useFileExplorerHandlers.ts
5962
1 src/renderer/src/components/right-sidebar/useFileExplorerInlineInput.ts
63+
1 src/renderer/src/components/settings/BrowserPane.tsx
6064
1 src/renderer/src/components/settings/CliSkillRuntimeSetup.tsx
6165
1 src/renderer/src/components/settings/GeneralPane.tsx
6266
2 src/renderer/src/components/settings/RepositoryGitHubAccountSection.tsx
@@ -68,12 +72,20 @@
6872
1 src/renderer/src/components/settings/use-settings-store-model.ts
6973
1 src/renderer/src/components/setup-guide/setup-guide-progress-readiness.ts
7074
2 src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.tsx
75+
1 src/renderer/src/components/sidebar/WorktreeList.tsx
7176
1 src/renderer/src/components/sidebar/add-repo-runtime-owner.ts
7277
1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts
78+
1 src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts
7379
2 src/renderer/src/components/sidebar/sidebar-host-options.ts
7480
1 src/renderer/src/components/sidebar/sidebar-project-drop.ts
81+
1 src/renderer/src/components/sidebar/use-add-repo-host-selection.ts
82+
1 src/renderer/src/components/sidebar/use-visible-workspace-kanban-worktree-ids.ts
83+
1 src/renderer/src/components/sidebar/visible-worktree-options-from-state.ts
84+
2 src/renderer/src/components/sidebar/visible-worktrees.ts
7585
1 src/renderer/src/components/sidebar/worktree-card-secondary-rows.tsx
76-
1 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts
86+
1 src/renderer/src/components/sidebar/worktree-filter-visibility.ts
87+
2 src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts
88+
2 src/renderer/src/components/sidebar/worktree-list/listing/use-folder-path-statuses.ts
7789
5 src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx
7890
1 src/renderer/src/components/status-bar/SshStatusSegment.tsx
7991
2 src/renderer/src/components/status-bar/provider-account-sync-key.ts
@@ -98,6 +110,8 @@
98110
2 src/renderer/src/components/terminal-pane/terminal-path-existence-batch.ts
99111
1 src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts
100112
1 src/renderer/src/components/use-task-page-github-issue-draft.ts
113+
1 src/renderer/src/components/use-task-page-source-availability.ts
114+
1 src/renderer/src/components/use-worktree-jump-palette-filter.ts
101115
2 src/renderer/src/components/use-worktree-jump-palette-quick-actions.ts
102116
3 src/renderer/src/hooks/composer-state/attachment-drop-state.ts
103117
1 src/renderer/src/hooks/composer-state/composer-navigation-actions.ts
@@ -109,7 +123,7 @@
109123
1 src/renderer/src/hooks/ipc-events/app-lifetime-ipc-bridge.ts
110124
1 src/renderer/src/hooks/ipc-events/runtime-environment-subscription-selection.ts
111125
1 src/renderer/src/hooks/ipc-events/settings-sidebar-ipc-bridge.ts
112-
1 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts
126+
3 src/renderer/src/hooks/use-active-skill-discovery-runtime-target.ts
113127
2 src/renderer/src/hooks/useGitHubRepoMetadata.ts
114128
4 src/renderer/src/hooks/useGitHubSlugMetadata.ts
115129
1 src/renderer/src/hooks/useIssueMetadata.ts
@@ -124,6 +138,7 @@
124138
1 src/renderer/src/lib/codex-pane-selection-lane.ts
125139
1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host
126140
1 src/renderer/src/lib/file-search-result-owner.ts
141+
2 src/renderer/src/lib/folder-workspace-runtime-owner.ts
127142
1 src/renderer/src/lib/github-pr-start-point.ts
128143
1 src/renderer/src/lib/github-source-runtime-context.ts
129144
1 src/renderer/src/lib/hosted-review-repo-read.ts
@@ -133,17 +148,19 @@
133148
2 src/renderer/src/lib/local-file-access.ts
134149
1 src/renderer/src/lib/local-preflight-context.ts
135150
2 src/renderer/src/lib/provider-runtime-context.ts
136-
1 src/renderer/src/lib/repo-runtime-owner.ts
151+
2 src/renderer/src/lib/repo-runtime-owner.ts
137152
1 src/renderer/src/lib/repo-slug-cache.ts
138153
1 src/renderer/src/lib/repo-slug-index.ts
139154
1 src/renderer/src/lib/resolve-owner.ts
140155
1 src/renderer/src/lib/runtime-session-mirror-owners.ts
141156
1 src/renderer/src/lib/single-runtime-legacy-owner.ts
142157
1 src/renderer/src/lib/structured-agent-session-tab-activation.ts
143-
2 src/renderer/src/lib/unread-badge-count-selector.ts
158+
2 src/renderer/src/lib/terminal-worktree-route.ts
159+
3 src/renderer/src/lib/unread-badge-count-selector.ts
144160
1 src/renderer/src/lib/workspace-file-host-routing.ts
145161
1 src/renderer/src/lib/worktree-creation-flow-startup.ts
146162
1 src/renderer/src/lib/worktree-operation-route.ts
163+
1 src/renderer/src/lib/worktree-runtime-owner.ts
147164
1 src/renderer/src/runtime/mobile-markdown-bridge.ts
148165
1 src/renderer/src/runtime/runtime-client-target.ts
149166
1 src/renderer/src/runtime/runtime-file-import-client.ts
@@ -167,7 +184,7 @@
167184
3 src/renderer/src/runtime/runtime-terminal-inspection.ts
168185
1 src/renderer/src/runtime/runtime-terminal-stream.ts
169186
1 src/renderer/src/runtime/runtime-terminal-verified-input.ts
170-
2 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts
187+
3 src/renderer/src/runtime/use-runtime-session-mirror-environment-key.ts
171188
1 src/renderer/src/runtime/web-runtime-browser-creation.ts
172189
1 src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts
173190
1 src/renderer/src/runtime/web-runtime-session-tab-move.ts
@@ -183,7 +200,8 @@
183200
1 src/renderer/src/store/github/project-actions.ts
184201
1 src/renderer/src/store/github/project-field-mutations.ts
185202
2 src/renderer/src/store/github/project-row-actions.ts
186-
1 src/renderer/src/store/github/repository-routing.ts
203+
2 src/renderer/src/store/github/repository-routing.ts
204+
1 src/renderer/src/store/github/visible-hosted-review-refresh-targets.ts
187205
1 src/renderer/src/store/github/work-item-mutation-actions.ts
188206
6 src/renderer/src/store/project-groups/nested-repository-operations.ts
189207
2 src/renderer/src/store/project-groups/project-group-catalog-actions.ts
@@ -196,28 +214,33 @@
196214
1 src/renderer/src/store/repos/repo-removal.ts
197215
1 src/renderer/src/store/repos/repo-update.ts
198216
2 src/renderer/src/store/repos/runtime-repo-catalog-actions.ts
217+
1 src/renderer/src/store/slices/browser/browser-host-state.ts
199218
1 src/renderer/src/store/slices/diff-comment-persistence.ts
200219
1 src/renderer/src/store/slices/editor/actions/markdown-link-action.ts
201220
1 src/renderer/src/store/slices/editor/actions/markdown-preview-actions.ts
202221
1 src/renderer/src/store/slices/editor/actions/open-file-apply.ts
203222
1 src/renderer/src/store/slices/github-cache-key.ts
223+
1 src/renderer/src/store/slices/hosted-review-cache-identity.ts
204224
4 src/renderer/src/store/slices/hosted-review.ts
205225
1 src/renderer/src/store/slices/preflight.ts
206226
1 src/renderer/src/store/slices/project-group-owner-routing.ts
227+
1 src/renderer/src/store/slices/repo-host-identity.ts
228+
1 src/renderer/src/store/slices/repo-reorder-host-split.ts
207229
1 src/renderer/src/store/slices/runtime-status.ts
208-
1 src/renderer/src/store/slices/settings.ts
230+
3 src/renderer/src/store/slices/settings.ts
209231
1 src/renderer/src/store/slices/worktree-visibility-owner-settings.ts
210232
3 src/renderer/src/store/slices/worktree-visibility-settings-write.ts
211233
1 src/renderer/src/store/slices/worktrees/create/create-worktree.ts
212234
1 src/renderer/src/store/slices/worktrees/create/prefetch-worktree-create-base.ts
213235
2 src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts
214236
1 src/renderer/src/store/slices/worktrees/listing/detected-worktree-refresh.ts
215-
1 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts
237+
2 src/renderer/src/store/slices/worktrees/listing/fetch-worktrees.ts
238+
1 src/renderer/src/store/slices/worktrees/listing/worktree-host-ownership.ts
216239
1 src/renderer/src/store/slices/worktrees/listing/worktree-owner-settings.ts
217240
2 src/renderer/src/store/slices/worktrees/metadata/hosted-review-push-target.ts
218241
1 src/renderer/src/store/slices/worktrees/metadata/update-worktree-meta.ts
219242
1 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts
220-
3 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts
243+
5 src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts
221244
1 src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.ts
222245
1 src/renderer/src/store/slices/worktrees/teardown/force-delete-preserved-branch.ts
223246
1 src/renderer/src/store/slices/worktrees/teardown/missing-worktree-terminal-teardown.ts

‎config/scripts/check-owner-routing-ratchet.mjs‎

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,52 @@ import { pathToFileURL } from 'node:url'
88
// instead of by the resource's own host. Per-file counts may only go down.
99
// - Owner routing: the three focus-routing helpers, counted together so renaming one into another
1010
// never lowers the count.
11-
// - Focus reads: every read of the setting, including the helpers and `defaultCreationHost`, so
12-
// swapping a helper for a direct read or for the creation default never lowers it either.
11+
// - Focus reads: every read of the setting, including helpers that read it for the caller
12+
// (`defaultCreationHost`, `getSettingsFocusedExecutionHostId`, …), so swapping one form for
13+
// another never lowers it either.
1314

1415
const SCAN_ROOT = 'src/renderer/src'
1516
const HELPER_NAMES = 'getActiveRuntimeTarget|legacyRouteFromSettings|settingsForRuntimeOwner'
1617
const HELPERS = `(?:${HELPER_NAMES})`
1718
const IMPORT_EXPORT_LIST = /\b(?:import|export)\s+(?:type\s+)?\{[^}]*\}/g
1819
// Calls and value uses (`.map(helper)`); definitions and type queries are not routing.
1920
const FOCUS_ROUTING_USE = new RegExp(`(?<!(?:function|typeof)\\s+)\\b${HELPERS}\\b`, 'g')
20-
const FOCUS_ROUTING_ALIAS = new RegExp(`\\b(?:${HELPER_NAMES}|defaultCreationHost)\\s+as\\b`)
21-
const FOCUS_SETTING_READ = /\??\.activeRuntimeEnvironmentId\b/g
22-
const DEFAULT_CREATION_HOST_USE = /(?<!(?:function|typeof)\s+)\bdefaultCreationHost\b/g
21+
// Helpers that read the setting on the caller's behalf, counted as reads by the focus ratchet.
22+
const FOCUS_READER_NAMES = new Set([
23+
...HELPER_NAMES.split('|'),
24+
'defaultCreationHost',
25+
'getSettingsFocusedExecutionHostId',
26+
'getSingleFocusedRuntimeEnvironmentId'
27+
])
28+
const FOCUS_ROUTING_ALIAS = new RegExp(`\\b(?:${[...FOCUS_READER_NAMES].join('|')})\\s+as\\b`)
2329

2430
/** Imports and re-exports are not uses; an aliased one is reported by {@link hasFocusRoutingAlias}. */
2531
export function countFocusRoutingCalls(sourceText) {
2632
return sourceText.replace(IMPORT_EXPORT_LIST, '').match(FOCUS_ROUTING_USE)?.length ?? 0
2733
}
2834

29-
/** Member reads of the setting plus every helper that reads it on the caller's behalf. */
35+
const FOCUS_READER_USE = new RegExp(
36+
`(?<!(?:function|typeof)\\s+)\\b(?:${[...FOCUS_READER_NAMES].join('|')})\\b`,
37+
'g'
38+
)
39+
// Element reads after a PascalCase name or `>` are indexed types (`GlobalSettings['…']`), not reads.
40+
const SETTING_MEMBER_READ =
41+
/\??\.\s*activeRuntimeEnvironmentId\b|(?<!(?:\b[A-Z][\w$]*|>)\s*)\[\s*['"]activeRuntimeEnvironmentId['"]\s*\]/g
42+
// `const { activeRuntimeEnvironmentId } = s` or `{ activeRuntimeEnvironmentId: id }: T = s`;
43+
// object literals and `({ … }) =>` parameters are not reads of the setting.
44+
const SETTING_DESTRUCTURE_READ =
45+
/\{[^{}]*\bactiveRuntimeEnvironmentId\b[^{}]*\}\s*(?::[^=;{}]*)?=(?![=>])/g
46+
47+
/**
48+
* Reads of the setting (member, element and destructuring reads) plus every helper that reads it
49+
* on the caller's behalf. Object-literal keys are writes and are not counted.
50+
*/
3051
export function countFocusSettingReads(sourceText) {
3152
const body = sourceText.replace(IMPORT_EXPORT_LIST, '')
3253
return (
33-
(body.match(FOCUS_SETTING_READ)?.length ?? 0) +
34-
(body.match(DEFAULT_CREATION_HOST_USE)?.length ?? 0) +
35-
countFocusRoutingCalls(sourceText)
54+
(body.match(SETTING_MEMBER_READ)?.length ?? 0) +
55+
(body.match(SETTING_DESTRUCTURE_READ)?.length ?? 0) +
56+
(body.match(FOCUS_READER_USE)?.length ?? 0)
3657
)
3758
}
3859

@@ -60,8 +81,8 @@ export const RATCHETS = [
6081
baselinePath: 'config/focus-setting-read-baseline.txt',
6182
count: countFocusSettingReads,
6283
header: [
63-
'# Renderer reads of the Active Server setting (`.activeRuntimeEnvironmentId`), the',
64-
'# focus-routing helpers and defaultCreationHost(, per file.',
84+
'# Renderer reads of the Active Server setting (member, element and destructuring reads of',
85+
'# activeRuntimeEnvironmentId) plus helpers that read it for the caller, per file.',
6586
'# This is a RATCHET: counts may only go DOWN. Only creation flows with no source row may read',
6687
'# the default host, through defaultCreationHost. Everything else routes by the owner.',
6788
'# Prune after removing reads: pnpm check:owner-routing-ratchet --prune'

‎config/scripts/check-owner-routing-ratchet.test.mjs‎

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,40 @@ describe('countFocusSettingReads', () => {
4747
"import { defaultCreationHost } from './default-creation-host'",
4848
'const a = settings?.activeRuntimeEnvironmentId',
4949
'const b = state.settings.activeRuntimeEnvironmentId',
50-
'const c = getActiveRuntimeTarget(settings)',
51-
'const d = defaultCreationHost(settings)',
50+
"const c = settings['activeRuntimeEnvironmentId']",
51+
'const d = getActiveRuntimeTarget(settings)',
52+
'const e = defaultCreationHost(settings)',
5253
'export function defaultCreationHost(settings) {}'
5354
].join('\n')
54-
expect(countFocusSettingReads(src)).toBe(4)
55+
expect(countFocusSettingReads(src)).toBe(5)
5556
})
5657

57-
it('does not count writes, keys or type positions', () => {
58+
it('counts a shared focus helper, so wrapping one in an owner transport lowers nothing', () => {
59+
expect(
60+
countFocusSettingReads(
61+
'const t = runtimeTargetForOwnerHostId(getSettingsFocusedExecutionHostId(s))'
62+
)
63+
).toBe(1)
64+
expect(countFocusSettingReads('const id = getSingleFocusedRuntimeEnvironmentId(state)')).toBe(1)
65+
})
66+
67+
it('counts destructuring reads', () => {
68+
expect(countFocusSettingReads('const { activeRuntimeEnvironmentId } = s')).toBe(1)
69+
expect(
70+
countFocusSettingReads(
71+
"const { theme, activeRuntimeEnvironmentId: id }: Pick<GlobalSettings, 'theme'> = s"
72+
)
73+
).toBe(1)
74+
})
75+
76+
it('does not count writes, keys, props or type positions', () => {
5877
const src = [
5978
'const owner = { activeRuntimeEnvironmentId: id }',
79+
'settings = { ...settings, activeRuntimeEnvironmentId: null }',
6080
"type T = GlobalSettings['activeRuntimeEnvironmentId']",
61-
'updateSettings({ activeRuntimeEnvironmentId: null })'
81+
'updateSettings({ activeRuntimeEnvironmentId: null })',
82+
'const f = ({ activeRuntimeEnvironmentId }) => activeRuntimeEnvironmentId',
83+
'if (a === b) { x = { activeRuntimeEnvironmentId } }'
6284
].join('\n')
6385
expect(countFocusSettingReads(src)).toBe(0)
6486
})
@@ -73,6 +95,9 @@ describe('hasFocusRoutingAlias', () => {
7395
hasFocusRoutingAlias("export {\n settingsForRuntimeOwner as owner\n} from './target'")
7496
).toBe(true)
7597
expect(hasFocusRoutingAlias("import { defaultCreationHost as host } from './d'")).toBe(true)
98+
expect(
99+
hasFocusRoutingAlias("import { getSettingsFocusedExecutionHostId as h } from './e'")
100+
).toBe(true)
76101
expect(hasFocusRoutingAlias("import { getActiveRuntimeTarget } from './rpc'")).toBe(false)
77102
})
78103
})

0 commit comments

Comments
 (0)